Stats and recommendations improvements

This commit is contained in:
Ilya Laktyushin 2023-11-19 19:10:13 +04:00
parent 13baadc3e7
commit cc2bd5ec92
43 changed files with 25474 additions and 25369 deletions

View File

@ -10508,3 +10508,9 @@ Sorry for the inconvenience.";
"Stats.StoryTitle" = "Story Statistics"; "Stats.StoryTitle" = "Story Statistics";
"Channel.Info.Settings" = "Channel Settings"; "Channel.Info.Settings" = "Channel Settings";
"Story.Context.ViewStats" = "View Statistics";
"Share.RepostStory" = "Repost\nStory";
"PeerInfo.PaneRecommended" = "Similar Channels";

View File

@ -933,7 +933,9 @@ public protocol SharedAccountContext: AnyObject {
func makeInstalledStickerPacksController(context: AccountContext, mode: InstalledStickerPacksControllerMode, forceTheme: PresentationTheme?) -> ViewController func makeInstalledStickerPacksController(context: AccountContext, mode: InstalledStickerPacksControllerMode, forceTheme: PresentationTheme?) -> ViewController
func makeChannelStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peerId: EnginePeer.Id, boosts: Bool, boostStatus: ChannelBoostStatus?, statsDatacenterId: Int32) -> ViewController func makeChannelStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peerId: EnginePeer.Id, boosts: Bool, boostStatus: ChannelBoostStatus?) -> ViewController
func makeMessagesStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, messageId: EngineMessage.Id) -> ViewController
func makeStoryStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peerId: EnginePeer.Id, storyId: Int32) -> ViewController
func makeDebugSettingsController(context: AccountContext?) -> ViewController? func makeDebugSettingsController(context: AccountContext?) -> ViewController?

View File

@ -989,14 +989,8 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
} }
let tooltipController = UndoOverlayController(presentationData: presentationData, content: .premiumPaywall(title: title, text: text, customUndoText: nil, timeout: nil, linkAction: { [weak navigationController] _ in let tooltipController = UndoOverlayController(presentationData: presentationData, content: .premiumPaywall(title: title, text: text, customUndoText: nil, timeout: nil, linkAction: { [weak navigationController] _ in
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.StatsDatacenterId(id: peerId)) let statsController = context.sharedContext.makeChannelStatsController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, boosts: true, boostStatus: nil)
|> deliverOnMainQueue).startStandalone(next: { [weak navigationController] statsDatacenterId in navigationController?.pushViewController(statsController)
guard let statsDatacenterId else {
return
}
let statsController = context.sharedContext.makeChannelStatsController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, boosts: true, boostStatus: nil, statsDatacenterId: statsDatacenterId)
navigationController?.pushViewController(statsController)
})
}), elevatedLayout: false, action: { _ in }), elevatedLayout: false, action: { _ in
return true return true
}) })
@ -1068,14 +1062,8 @@ public func createGiveawayController(context: AccountContext, updatedPresentatio
let text = presentationData.strings.BoostGift_GiveawayCreated_Text let text = presentationData.strings.BoostGift_GiveawayCreated_Text
let tooltipController = UndoOverlayController(presentationData: presentationData, content: .premiumPaywall(title: title, text: text, customUndoText: nil, timeout: nil, linkAction: { [weak navigationController] _ in let tooltipController = UndoOverlayController(presentationData: presentationData, content: .premiumPaywall(title: title, text: text, customUndoText: nil, timeout: nil, linkAction: { [weak navigationController] _ in
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.StatsDatacenterId(id: peerId)) let statsController = context.sharedContext.makeChannelStatsController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, boosts: true, boostStatus: nil)
|> deliverOnMainQueue).startStandalone(next: { [weak navigationController] statsDatacenterId in navigationController?.pushViewController(statsController)
guard let statsDatacenterId else {
return
}
let statsController = context.sharedContext.makeChannelStatsController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, boosts: true, boostStatus: nil, statsDatacenterId: statsDatacenterId)
navigationController?.pushViewController(statsController)
})
}), elevatedLayout: false, action: { _ in }), elevatedLayout: false, action: { _ in
return true return true
}) })

View File

@ -155,7 +155,7 @@ private enum StatsEntry: ItemListNodeEntry {
case instantPageInteractionsGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, StatsGraph, ChartType) case instantPageInteractionsGraph(PresentationTheme, PresentationStrings, PresentationDateTimeFormat, StatsGraph, ChartType)
case postsTitle(PresentationTheme, String) case postsTitle(PresentationTheme, String)
case post(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, StatsPostItem, ChannelStatsMessageInteractions) case post(Int32, PresentationTheme, PresentationStrings, PresentationDateTimeFormat, Peer, StatsPostItem, ChannelStatsPostInteractions)
case boostLevel(PresentationTheme, Int32, Int32, CGFloat) case boostLevel(PresentationTheme, Int32, Int32, CGFloat)
@ -816,7 +816,7 @@ private struct ChannelStatsControllerState: Equatable {
} }
private func channelStatsControllerEntries(state: ChannelStatsControllerState, peer: EnginePeer?, data: ChannelStats?, messages: [Message]?, stories: PeerStoryListContext.State?, interactions: [MessageId: ChannelStatsMessageInteractions]?, boostData: ChannelBoostStatus?, boostersState: ChannelBoostersContext.State?, giftsState: ChannelBoostersContext.State?, presentationData: PresentationData, giveawayAvailable: Bool) -> [StatsEntry] { private func channelStatsControllerEntries(state: ChannelStatsControllerState, peer: EnginePeer?, data: ChannelStats?, messages: [Message]?, stories: PeerStoryListContext.State?, interactions: [ChannelStatsPostInteractions.PostId: ChannelStatsPostInteractions]?, boostData: ChannelBoostStatus?, boostersState: ChannelBoostersContext.State?, giftsState: ChannelBoostersContext.State?, presentationData: PresentationData, giveawayAvailable: Bool) -> [StatsEntry] {
var entries: [StatsEntry] = [] var entries: [StatsEntry] = []
switch state.section { switch state.section {
@ -889,34 +889,40 @@ private func channelStatsControllerEntries(state: ChannelStatsControllerState, p
} }
var posts: [StatsPostItem] = [] if let peer, let interactions {
if let messages, let interactions { var posts: [StatsPostItem] = []
for message in messages { if let messages {
if let _ = interactions[message.id] { for message in messages {
posts.append(.message(message)) if let _ = interactions[.message(id: message.id)] {
} posts.append(.message(message))
}
}
if let stories {
for story in stories.items {
posts.append(.story(story))
}
}
posts.sort(by: { $0.timestamp > $1.timestamp })
if !posts.isEmpty, let interactions, let peer = peer?._asPeer() {
entries.append(.postsTitle(presentationData.theme, presentationData.strings.Stats_PostsTitle))
var index: Int32 = 0
for post in posts {
switch post {
case let .message(message):
if let interactions = interactions[message.id] {
entries.append(.post(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, post, interactions))
} }
case let .story(story):
entries.append(.post(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer, post, ChannelStatsMessageInteractions(messageId: MessageId(peerId: PeerId(0), namespace: 0, id: 0), views: Int32(story.views?.seenCount ?? 0), forwards: Int32(story.views?.forwardCount ?? 0), reactions: Int32(story.views?.reactedCount ?? 0))))
} }
index += 1 }
if let stories {
for story in stories.items {
if let _ = interactions[.story(peerId: peer.id, id: story.id)] {
posts.append(.story(story))
}
}
}
posts.sort(by: { $0.timestamp > $1.timestamp })
if !posts.isEmpty {
entries.append(.postsTitle(presentationData.theme, presentationData.strings.Stats_PostsTitle))
var index: Int32 = 0
for post in posts {
switch post {
case let .message(message):
if let interactions = interactions[.message(id: message.id)] {
entries.append(.post(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer._asPeer(), post, interactions))
}
case let .story(story):
if let interactions = interactions[.story(peerId: peer.id, id: story.id)] {
entries.append(.post(index, presentationData.theme, presentationData.strings, presentationData.dateTimeFormat, peer._asPeer(), post, interactions))
}
}
index += 1
}
} }
} }
} }
@ -1032,7 +1038,7 @@ private func channelStatsControllerEntries(state: ChannelStatsControllerState, p
return entries return entries
} }
public func channelStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: PeerId, section: ChannelStatsSection = .stats, boostStatus: ChannelBoostStatus? = nil, statsDatacenterId: Int32?) -> ViewController { public func channelStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: PeerId, section: ChannelStatsSection = .stats, boostStatus: ChannelBoostStatus? = 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), 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))
let updateState: ((ChannelStatsControllerState) -> ChannelStatsControllerState) -> Void = { f in let updateState: ((ChannelStatsControllerState) -> ChannelStatsControllerState) -> Void = { f in
@ -1049,10 +1055,8 @@ public func channelStatsController(context: AccountContext, updatedPresentationD
let messagesPromise = Promise<MessageHistoryView?>(nil) let messagesPromise = Promise<MessageHistoryView?>(nil)
let storiesPromise = Promise<PeerStoryListContext.State?>() let storiesPromise = Promise<PeerStoryListContext.State?>()
let datacenterId: Int32 = statsDatacenterId ?? 0 let statsContext = ChannelStatsContext(postbox: context.account.postbox, network: context.account.network, peerId: peerId)
let statsContext = ChannelStatsContext(postbox: context.account.postbox, network: context.account.network, datacenterId: datacenterId, peerId: peerId)
let dataSignal: Signal<ChannelStats?, NoError> = statsContext.state let dataSignal: Signal<ChannelStats?, NoError> = statsContext.state
|> map { state in |> map { state in
return state.stats return state.stats
@ -1251,9 +1255,9 @@ public func channelStatsController(context: AccountContext, updatedPresentationD
let messages = messageView?.entries.map { $0.message }.sorted(by: { (lhsMessage, rhsMessage) -> Bool in let messages = messageView?.entries.map { $0.message }.sorted(by: { (lhsMessage, rhsMessage) -> Bool in
return lhsMessage.timestamp > rhsMessage.timestamp return lhsMessage.timestamp > rhsMessage.timestamp
}) })
let interactions = data?.messageInteractions.reduce([MessageId : ChannelStatsMessageInteractions]()) { (map, interactions) -> [MessageId : ChannelStatsMessageInteractions] in let interactions = data?.postInteractions.reduce([ChannelStatsPostInteractions.PostId : ChannelStatsPostInteractions]()) { (map, interactions) -> [ChannelStatsPostInteractions.PostId : ChannelStatsPostInteractions] in
var map = map var map = map
map[interactions.messageId] = interactions map[interactions.postId] = interactions
return map return map
} }
@ -1288,9 +1292,9 @@ public func channelStatsController(context: AccountContext, updatedPresentationD
case let .message(message): case let .message(message):
subject = .message(id: message.id) subject = .message(id: message.id)
case let .story(story): case let .story(story):
subject = .story(peer: peer, storyItem: story) subject = .story(peerId: peerId, id: story.id)
} }
controller?.push(messageStatsController(context: context, subject: subject, statsDatacenterId: statsDatacenterId)) controller?.push(messageStatsController(context: context, subject: subject))
} }
contextActionImpl = { [weak controller] messageId, sourceNode, gesture in contextActionImpl = { [weak controller] messageId, sourceNode, gesture in
guard let controller = controller, let sourceNode = sourceNode as? ContextExtractedContentContainingNode else { guard let controller = controller, let sourceNode = sourceNode as? ContextExtractedContentContainingNode else {

View File

@ -707,7 +707,7 @@ private func canEditAdminRights(accountPeerId: EnginePeer.Id, channelPeer: Engin
} }
} }
public func groupStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: EnginePeer.Id, statsDatacenterId: Int32?) -> ViewController { public func groupStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, peerId: EnginePeer.Id) -> ViewController {
let statePromise = ValuePromise(GroupStatsState()) let statePromise = ValuePromise(GroupStatsState())
let stateValue = Atomic(value: GroupStatsState()) let stateValue = Atomic(value: GroupStatsState())
let updateState: ((GroupStatsState) -> GroupStatsState) -> Void = { f in let updateState: ((GroupStatsState) -> GroupStatsState) -> Void = { f in
@ -718,8 +718,6 @@ public func groupStatsController(context: AccountContext, updatedPresentationDat
let dataPromise = Promise<GroupStats?>(nil) let dataPromise = Promise<GroupStats?>(nil)
let peersPromise = Promise<[EnginePeer.Id: EnginePeer]?>(nil) let peersPromise = Promise<[EnginePeer.Id: EnginePeer]?>(nil)
let datacenterId: Int32 = statsDatacenterId ?? 0
var openPeerImpl: ((EnginePeer) -> Void)? var openPeerImpl: ((EnginePeer) -> Void)?
var openPeerHistoryImpl: ((EnginePeer.Id) -> Void)? var openPeerHistoryImpl: ((EnginePeer.Id) -> Void)?
var openPeerAdminActionsImpl: ((EnginePeer.Id) -> Void)? var openPeerAdminActionsImpl: ((EnginePeer.Id) -> Void)?
@ -727,7 +725,7 @@ public func groupStatsController(context: AccountContext, updatedPresentationDat
actionsDisposable.add(context.account.viewTracker.peerView(peerId, updateData: true).start()) actionsDisposable.add(context.account.viewTracker.peerView(peerId, updateData: true).start())
let statsContext = GroupStatsContext(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, datacenterId: datacenterId, peerId: peerId) let statsContext = GroupStatsContext(postbox: context.account.postbox, network: context.account.network, accountPeerId: context.account.peerId, peerId: peerId)
let dataSignal: Signal<GroupStats?, NoError> = statsContext.state let dataSignal: Signal<GroupStats?, NoError> = statsContext.state
|> map { state in |> map { state in
return state.stats return state.stats

View File

@ -221,7 +221,7 @@ private func messageStatsControllerEntries(data: PostStats?, messages: SearchMes
public enum StatsSubject { public enum StatsSubject {
case message(id: EngineMessage.Id) case message(id: EngineMessage.Id)
case story(peer: EnginePeer, storyItem: EngineStoryItem) case story(peerId: EnginePeer.Id, id: Int32)
} }
protocol PostStats { protocol PostStats {
@ -240,21 +240,19 @@ extension StoryStats: PostStats {
} }
public func messageStatsController(context: AccountContext, subject: StatsSubject, statsDatacenterId: Int32?) -> ViewController { public func messageStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, subject: StatsSubject) -> ViewController {
var navigateToMessageImpl: ((EngineMessage.Id) -> Void)? var navigateToMessageImpl: ((EngineMessage.Id) -> Void)?
let actionsDisposable = DisposableSet() let actionsDisposable = DisposableSet()
let dataPromise = Promise<PostStats?>(nil) let dataPromise = Promise<PostStats?>(nil)
let messagesPromise = Promise<(SearchMessagesResult, SearchMessagesState)?>(nil) let messagesPromise = Promise<(SearchMessagesResult, SearchMessagesState)?>(nil)
let datacenterId: Int32 = statsDatacenterId ?? 0
let anyStatsContext: Any let anyStatsContext: Any
let dataSignal: Signal<PostStats?, NoError> let dataSignal: Signal<PostStats?, NoError>
var loadDetailedGraphImpl: ((StatsGraph, Int64) -> Signal<StatsGraph?, NoError>)? var loadDetailedGraphImpl: ((StatsGraph, Int64) -> Signal<StatsGraph?, NoError>)?
switch subject { switch subject {
case let .message(id): case let .message(id):
let statsContext = MessageStatsContext(postbox: context.account.postbox, network: context.account.network, datacenterId: datacenterId, messageId: id) let statsContext = MessageStatsContext(postbox: context.account.postbox, network: context.account.network, messageId: id)
loadDetailedGraphImpl = { [weak statsContext] graph, x in loadDetailedGraphImpl = { [weak statsContext] graph, x in
return statsContext?.loadDetailedGraph(graph, x: x) ?? .single(nil) return statsContext?.loadDetailedGraph(graph, x: x) ?? .single(nil)
} }
@ -264,8 +262,8 @@ public func messageStatsController(context: AccountContext, subject: StatsSubjec
} }
dataPromise.set(.single(nil) |> then(dataSignal)) dataPromise.set(.single(nil) |> then(dataSignal))
anyStatsContext = statsContext anyStatsContext = statsContext
case let .story(peer, storyItem): case let .story(peerId, id):
let statsContext = StoryStatsContext(postbox: context.account.postbox, network: context.account.network, datacenterId: datacenterId, peerId: peer.id, storyId: storyItem.id) let statsContext = StoryStatsContext(postbox: context.account.postbox, network: context.account.network, peerId: peerId, storyId: id)
loadDetailedGraphImpl = { [weak statsContext] graph, x in loadDetailedGraphImpl = { [weak statsContext] graph, x in
return statsContext?.loadDetailedGraph(graph, x: x) ?? .single(nil) return statsContext?.loadDetailedGraph(graph, x: x) ?? .single(nil)
} }
@ -288,7 +286,7 @@ public func messageStatsController(context: AccountContext, subject: StatsSubjec
let previousData = Atomic<PostStats?>(value: nil) let previousData = Atomic<PostStats?>(value: nil)
if case let .message(id) = subject { if case let .message(id) = subject {
let searchSignal = context.engine.messages.searchMessages(location: .publicForwards(messageId: id, datacenterId: Int(datacenterId)), query: "", state: nil) let searchSignal = context.engine.messages.searchMessages(location: .publicForwards(messageId: id), query: "", state: nil)
|> map(Optional.init) |> map(Optional.init)
|> afterNext { result in |> afterNext { result in
if let result = result { if let result = result {
@ -304,17 +302,31 @@ public func messageStatsController(context: AccountContext, subject: StatsSubjec
messagesPromise.set(.single(nil)) messagesPromise.set(.single(nil))
} }
let iconNode: ASDisplayNode? let iconNodePromise = Promise<ASDisplayNode?>()
if case let .story(peer, storyItem) = subject { if case let .story(peerId, id) = subject {
let presentationData = context.sharedContext.currentPresentationData.with { $0 } let _ = id
iconNode = StoryIconNode(context: context, theme: presentationData.theme, peer: peer._asPeer(), storyItem: storyItem) iconNodePromise.set(
context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId))
|> deliverOnMainQueue
|> map { peer -> ASDisplayNode? in
if let _ = peer?._asPeer() {
// let presentationData = context.sharedContext.currentPresentationData.with { $0 }
// return StoryIconNode(context: context, theme: presentationData.theme, peer: peer, storyItem: storyItem)
return nil
} else {
return nil
}
}
)
} else { } else {
iconNode = nil iconNodePromise.set(.single(nil))
} }
let signal = combineLatest(context.sharedContext.presentationData, dataPromise.get(), messagesPromise.get(), longLoadingSignal) let presentationData = updatedPresentationData?.signal ?? context.sharedContext.presentationData
let signal = combineLatest(presentationData, dataPromise.get(), messagesPromise.get(), longLoadingSignal, iconNodePromise.get())
|> deliverOnMainQueue |> deliverOnMainQueue
|> map { presentationData, data, search, longLoading -> (ItemListControllerState, (ItemListNodeState, Any)) in |> map { presentationData, data, search, longLoading, iconNode -> (ItemListControllerState, (ItemListNodeState, Any)) in
let previous = previousData.swap(data) let previous = previousData.swap(data)
var emptyStateItem: ItemListControllerEmptyStateItem? var emptyStateItem: ItemListControllerEmptyStateItem?
if data == nil { if data == nil {

View File

@ -173,7 +173,14 @@ class MessageStatsOverviewItemNode: ListViewItemNode {
leftTitleLabelLayoutAndApply = makeLeftTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_Message_Views, font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: min(maxItemWidth, remainingWidth), height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) leftTitleLabelLayoutAndApply = makeLeftTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_Message_Views, font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: min(maxItemWidth, remainingWidth), height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
remainingWidth -= leftTitleLabelLayoutAndApply!.0.size.width - 4.0 remainingWidth -= leftTitleLabelLayoutAndApply!.0.size.width - 4.0
centerTitleLabelLayoutAndApply = makeCenterTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_Message_PublicShares, font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: min(maxItemWidth, remainingWidth), height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) let centerTitle: String
if let _ = item.stats as? StoryStats {
centerTitle = "Reactions"
} else {
centerTitle = item.presentationData.strings.Stats_Message_PublicShares
}
centerTitleLabelLayoutAndApply = makeCenterTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: centerTitle, font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: min(maxItemWidth, remainingWidth), height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))
remainingWidth -= centerTitleLabelLayoutAndApply!.0.size.width - 4.0 remainingWidth -= centerTitleLabelLayoutAndApply!.0.size.width - 4.0
rightTitleLabelLayoutAndApply = makeRightTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_Message_PrivateShares, font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: min(maxItemWidth, remainingWidth), height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) rightTitleLabelLayoutAndApply = makeRightTitleLabelLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: item.presentationData.strings.Stats_Message_PrivateShares, font: titleFont, textColor: item.presentationData.theme.list.sectionHeaderTextColor), backgroundColor: nil, maximumNumberOfLines: 2, truncationType: .end, constrainedSize: CGSize(width: min(maxItemWidth, remainingWidth), height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets()))

View File

@ -535,7 +535,6 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-297296796] = { return Api.MessageExtendedMedia.parse_messageExtendedMedia($0) } dict[-297296796] = { return Api.MessageExtendedMedia.parse_messageExtendedMedia($0) }
dict[-1386050360] = { return Api.MessageExtendedMedia.parse_messageExtendedMediaPreview($0) } dict[-1386050360] = { return Api.MessageExtendedMedia.parse_messageExtendedMediaPreview($0) }
dict[1601666510] = { return Api.MessageFwdHeader.parse_messageFwdHeader($0) } dict[1601666510] = { return Api.MessageFwdHeader.parse_messageFwdHeader($0) }
dict[-1387279939] = { return Api.MessageInteractionCounters.parse_messageInteractionCounters($0) }
dict[1882335561] = { return Api.MessageMedia.parse_messageMediaContact($0) } dict[1882335561] = { return Api.MessageMedia.parse_messageMediaContact($0) }
dict[1065280907] = { return Api.MessageMedia.parse_messageMediaDice($0) } dict[1065280907] = { return Api.MessageMedia.parse_messageMediaDice($0) }
dict[1291114285] = { return Api.MessageMedia.parse_messageMediaDocument($0) } dict[1291114285] = { return Api.MessageMedia.parse_messageMediaDocument($0) }
@ -669,6 +668,8 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[2061444128] = { return Api.PollResults.parse_pollResults($0) } dict[2061444128] = { return Api.PollResults.parse_pollResults($0) }
dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) } dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) }
dict[512535275] = { return Api.PostAddress.parse_postAddress($0) } dict[512535275] = { return Api.PostAddress.parse_postAddress($0) }
dict[-419066241] = { return Api.PostInteractionCounters.parse_postInteractionCountersMessage($0) }
dict[-1974989273] = { return Api.PostInteractionCounters.parse_postInteractionCountersStory($0) }
dict[629052971] = { return Api.PremiumGiftCodeOption.parse_premiumGiftCodeOption($0) } dict[629052971] = { return Api.PremiumGiftCodeOption.parse_premiumGiftCodeOption($0) }
dict[1958953753] = { return Api.PremiumGiftOption.parse_premiumGiftOption($0) } dict[1958953753] = { return Api.PremiumGiftOption.parse_premiumGiftOption($0) }
dict[1596792306] = { return Api.PremiumSubscriptionOption.parse_premiumSubscriptionOption($0) } dict[1596792306] = { return Api.PremiumSubscriptionOption.parse_premiumSubscriptionOption($0) }
@ -1187,7 +1188,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[-2030542532] = { return Api.premium.BoostsList.parse_boostsList($0) } dict[-2030542532] = { return Api.premium.BoostsList.parse_boostsList($0) }
dict[1230586490] = { return Api.premium.BoostsStatus.parse_boostsStatus($0) } dict[1230586490] = { return Api.premium.BoostsStatus.parse_boostsStatus($0) }
dict[-1696454430] = { return Api.premium.MyBoosts.parse_myBoosts($0) } dict[-1696454430] = { return Api.premium.MyBoosts.parse_myBoosts($0) }
dict[-886032030] = { return Api.stats.BroadcastStats.parse_broadcastStats($0) } dict[963421692] = { return Api.stats.BroadcastStats.parse_broadcastStats($0) }
dict[-276825834] = { return Api.stats.MegagroupStats.parse_megagroupStats($0) } dict[-276825834] = { return Api.stats.MegagroupStats.parse_megagroupStats($0) }
dict[2145983508] = { return Api.stats.MessageStats.parse_messageStats($0) } dict[2145983508] = { return Api.stats.MessageStats.parse_messageStats($0) }
dict[1355613820] = { return Api.stats.StoryStats.parse_storyStats($0) } dict[1355613820] = { return Api.stats.StoryStats.parse_storyStats($0) }
@ -1593,8 +1594,6 @@ public extension Api {
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.MessageFwdHeader: case let _1 as Api.MessageFwdHeader:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.MessageInteractionCounters:
_1.serialize(buffer, boxed)
case let _1 as Api.MessageMedia: case let _1 as Api.MessageMedia:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.MessagePeerReaction: case let _1 as Api.MessagePeerReaction:
@ -1683,6 +1682,8 @@ public extension Api {
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.PostAddress: case let _1 as Api.PostAddress:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.PostInteractionCounters:
_1.serialize(buffer, boxed)
case let _1 as Api.PremiumGiftCodeOption: case let _1 as Api.PremiumGiftCodeOption:
_1.serialize(buffer, boxed) _1.serialize(buffer, boxed)
case let _1 as Api.PremiumGiftOption: case let _1 as Api.PremiumGiftOption:

View File

@ -688,50 +688,6 @@ public extension Api {
} }
} }
public extension Api {
enum MessageInteractionCounters: TypeConstructorDescription {
case messageInteractionCounters(msgId: Int32, views: Int32, forwards: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .messageInteractionCounters(let msgId, let views, let forwards):
if boxed {
buffer.appendInt32(-1387279939)
}
serializeInt32(msgId, buffer: buffer, boxed: false)
serializeInt32(views, buffer: buffer, boxed: false)
serializeInt32(forwards, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .messageInteractionCounters(let msgId, let views, let forwards):
return ("messageInteractionCounters", [("msgId", msgId as Any), ("views", views as Any), ("forwards", forwards as Any)])
}
}
public static func parse_messageInteractionCounters(_ reader: BufferReader) -> MessageInteractionCounters? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _3: Int32?
_3 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
if _c1 && _c2 && _c3 {
return Api.MessageInteractionCounters.messageInteractionCounters(msgId: _1!, views: _2!, forwards: _3!)
}
else {
return nil
}
}
}
}
public extension Api { public extension Api {
indirect enum MessageMedia: TypeConstructorDescription { indirect enum MessageMedia: TypeConstructorDescription {
case messageMediaContact(phoneNumber: String, firstName: String, lastName: String, vcard: String, userId: Int64) case messageMediaContact(phoneNumber: String, firstName: String, lastName: String, vcard: String, userId: Int64)

View File

@ -750,6 +750,86 @@ public extension Api {
} }
} }
public extension Api {
enum PostInteractionCounters: TypeConstructorDescription {
case postInteractionCountersMessage(msgId: Int32, views: Int32, forwards: Int32, reactions: Int32)
case postInteractionCountersStory(storyId: Int32, views: Int32, forwards: Int32, reactions: Int32)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .postInteractionCountersMessage(let msgId, let views, let forwards, let reactions):
if boxed {
buffer.appendInt32(-419066241)
}
serializeInt32(msgId, buffer: buffer, boxed: false)
serializeInt32(views, buffer: buffer, boxed: false)
serializeInt32(forwards, buffer: buffer, boxed: false)
serializeInt32(reactions, buffer: buffer, boxed: false)
break
case .postInteractionCountersStory(let storyId, let views, let forwards, let reactions):
if boxed {
buffer.appendInt32(-1974989273)
}
serializeInt32(storyId, buffer: buffer, boxed: false)
serializeInt32(views, buffer: buffer, boxed: false)
serializeInt32(forwards, buffer: buffer, boxed: false)
serializeInt32(reactions, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .postInteractionCountersMessage(let msgId, let views, let forwards, let reactions):
return ("postInteractionCountersMessage", [("msgId", msgId as Any), ("views", views as Any), ("forwards", forwards as Any), ("reactions", reactions as Any)])
case .postInteractionCountersStory(let storyId, let views, let forwards, let reactions):
return ("postInteractionCountersStory", [("storyId", storyId as Any), ("views", views as Any), ("forwards", forwards as Any), ("reactions", reactions as Any)])
}
}
public static func parse_postInteractionCountersMessage(_ reader: BufferReader) -> PostInteractionCounters? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _3: Int32?
_3 = reader.readInt32()
var _4: Int32?
_4 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.PostInteractionCounters.postInteractionCountersMessage(msgId: _1!, views: _2!, forwards: _3!, reactions: _4!)
}
else {
return nil
}
}
public static func parse_postInteractionCountersStory(_ reader: BufferReader) -> PostInteractionCounters? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Int32?
_2 = reader.readInt32()
var _3: Int32?
_3 = reader.readInt32()
var _4: Int32?
_4 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.PostInteractionCounters.postInteractionCountersStory(storyId: _1!, views: _2!, forwards: _3!, reactions: _4!)
}
else {
return nil
}
}
}
}
public extension Api { public extension Api {
enum PremiumGiftCodeOption: TypeConstructorDescription { enum PremiumGiftCodeOption: TypeConstructorDescription {
case premiumGiftCodeOption(flags: Int32, users: Int32, months: Int32, storeProduct: String?, storeQuantity: Int32?, currency: String, amount: Int64) case premiumGiftCodeOption(flags: Int32, users: Int32, months: Int32, storeProduct: String?, storeQuantity: Int32?, currency: String, amount: Int64)
@ -1110,183 +1190,3 @@ public extension Api {
} }
} }
public extension Api {
enum PrivacyRule: TypeConstructorDescription {
case privacyValueAllowAll
case privacyValueAllowChatParticipants(chats: [Int64])
case privacyValueAllowCloseFriends
case privacyValueAllowContacts
case privacyValueAllowUsers(users: [Int64])
case privacyValueDisallowAll
case privacyValueDisallowChatParticipants(chats: [Int64])
case privacyValueDisallowContacts
case privacyValueDisallowUsers(users: [Int64])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .privacyValueAllowAll:
if boxed {
buffer.appendInt32(1698855810)
}
break
case .privacyValueAllowChatParticipants(let chats):
if boxed {
buffer.appendInt32(1796427406)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(chats.count))
for item in chats {
serializeInt64(item, buffer: buffer, boxed: false)
}
break
case .privacyValueAllowCloseFriends:
if boxed {
buffer.appendInt32(-135735141)
}
break
case .privacyValueAllowContacts:
if boxed {
buffer.appendInt32(-123988)
}
break
case .privacyValueAllowUsers(let users):
if boxed {
buffer.appendInt32(-1198497870)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(users.count))
for item in users {
serializeInt64(item, buffer: buffer, boxed: false)
}
break
case .privacyValueDisallowAll:
if boxed {
buffer.appendInt32(-1955338397)
}
break
case .privacyValueDisallowChatParticipants(let chats):
if boxed {
buffer.appendInt32(1103656293)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(chats.count))
for item in chats {
serializeInt64(item, buffer: buffer, boxed: false)
}
break
case .privacyValueDisallowContacts:
if boxed {
buffer.appendInt32(-125240806)
}
break
case .privacyValueDisallowUsers(let users):
if boxed {
buffer.appendInt32(-463335103)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(users.count))
for item in users {
serializeInt64(item, buffer: buffer, boxed: false)
}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .privacyValueAllowAll:
return ("privacyValueAllowAll", [])
case .privacyValueAllowChatParticipants(let chats):
return ("privacyValueAllowChatParticipants", [("chats", chats as Any)])
case .privacyValueAllowCloseFriends:
return ("privacyValueAllowCloseFriends", [])
case .privacyValueAllowContacts:
return ("privacyValueAllowContacts", [])
case .privacyValueAllowUsers(let users):
return ("privacyValueAllowUsers", [("users", users as Any)])
case .privacyValueDisallowAll:
return ("privacyValueDisallowAll", [])
case .privacyValueDisallowChatParticipants(let chats):
return ("privacyValueDisallowChatParticipants", [("chats", chats as Any)])
case .privacyValueDisallowContacts:
return ("privacyValueDisallowContacts", [])
case .privacyValueDisallowUsers(let users):
return ("privacyValueDisallowUsers", [("users", users as Any)])
}
}
public static func parse_privacyValueAllowAll(_ reader: BufferReader) -> PrivacyRule? {
return Api.PrivacyRule.privacyValueAllowAll
}
public static func parse_privacyValueAllowChatParticipants(_ reader: BufferReader) -> PrivacyRule? {
var _1: [Int64]?
if let _ = reader.readInt32() {
_1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self)
}
let _c1 = _1 != nil
if _c1 {
return Api.PrivacyRule.privacyValueAllowChatParticipants(chats: _1!)
}
else {
return nil
}
}
public static func parse_privacyValueAllowCloseFriends(_ reader: BufferReader) -> PrivacyRule? {
return Api.PrivacyRule.privacyValueAllowCloseFriends
}
public static func parse_privacyValueAllowContacts(_ reader: BufferReader) -> PrivacyRule? {
return Api.PrivacyRule.privacyValueAllowContacts
}
public static func parse_privacyValueAllowUsers(_ reader: BufferReader) -> PrivacyRule? {
var _1: [Int64]?
if let _ = reader.readInt32() {
_1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self)
}
let _c1 = _1 != nil
if _c1 {
return Api.PrivacyRule.privacyValueAllowUsers(users: _1!)
}
else {
return nil
}
}
public static func parse_privacyValueDisallowAll(_ reader: BufferReader) -> PrivacyRule? {
return Api.PrivacyRule.privacyValueDisallowAll
}
public static func parse_privacyValueDisallowChatParticipants(_ reader: BufferReader) -> PrivacyRule? {
var _1: [Int64]?
if let _ = reader.readInt32() {
_1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self)
}
let _c1 = _1 != nil
if _c1 {
return Api.PrivacyRule.privacyValueDisallowChatParticipants(chats: _1!)
}
else {
return nil
}
}
public static func parse_privacyValueDisallowContacts(_ reader: BufferReader) -> PrivacyRule? {
return Api.PrivacyRule.privacyValueDisallowContacts
}
public static func parse_privacyValueDisallowUsers(_ reader: BufferReader) -> PrivacyRule? {
var _1: [Int64]?
if let _ = reader.readInt32() {
_1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self)
}
let _c1 = _1 != nil
if _c1 {
return Api.PrivacyRule.privacyValueDisallowUsers(users: _1!)
}
else {
return nil
}
}
}
}

View File

@ -1,3 +1,183 @@
public extension Api {
enum PrivacyRule: TypeConstructorDescription {
case privacyValueAllowAll
case privacyValueAllowChatParticipants(chats: [Int64])
case privacyValueAllowCloseFriends
case privacyValueAllowContacts
case privacyValueAllowUsers(users: [Int64])
case privacyValueDisallowAll
case privacyValueDisallowChatParticipants(chats: [Int64])
case privacyValueDisallowContacts
case privacyValueDisallowUsers(users: [Int64])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .privacyValueAllowAll:
if boxed {
buffer.appendInt32(1698855810)
}
break
case .privacyValueAllowChatParticipants(let chats):
if boxed {
buffer.appendInt32(1796427406)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(chats.count))
for item in chats {
serializeInt64(item, buffer: buffer, boxed: false)
}
break
case .privacyValueAllowCloseFriends:
if boxed {
buffer.appendInt32(-135735141)
}
break
case .privacyValueAllowContacts:
if boxed {
buffer.appendInt32(-123988)
}
break
case .privacyValueAllowUsers(let users):
if boxed {
buffer.appendInt32(-1198497870)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(users.count))
for item in users {
serializeInt64(item, buffer: buffer, boxed: false)
}
break
case .privacyValueDisallowAll:
if boxed {
buffer.appendInt32(-1955338397)
}
break
case .privacyValueDisallowChatParticipants(let chats):
if boxed {
buffer.appendInt32(1103656293)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(chats.count))
for item in chats {
serializeInt64(item, buffer: buffer, boxed: false)
}
break
case .privacyValueDisallowContacts:
if boxed {
buffer.appendInt32(-125240806)
}
break
case .privacyValueDisallowUsers(let users):
if boxed {
buffer.appendInt32(-463335103)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(users.count))
for item in users {
serializeInt64(item, buffer: buffer, boxed: false)
}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .privacyValueAllowAll:
return ("privacyValueAllowAll", [])
case .privacyValueAllowChatParticipants(let chats):
return ("privacyValueAllowChatParticipants", [("chats", chats as Any)])
case .privacyValueAllowCloseFriends:
return ("privacyValueAllowCloseFriends", [])
case .privacyValueAllowContacts:
return ("privacyValueAllowContacts", [])
case .privacyValueAllowUsers(let users):
return ("privacyValueAllowUsers", [("users", users as Any)])
case .privacyValueDisallowAll:
return ("privacyValueDisallowAll", [])
case .privacyValueDisallowChatParticipants(let chats):
return ("privacyValueDisallowChatParticipants", [("chats", chats as Any)])
case .privacyValueDisallowContacts:
return ("privacyValueDisallowContacts", [])
case .privacyValueDisallowUsers(let users):
return ("privacyValueDisallowUsers", [("users", users as Any)])
}
}
public static func parse_privacyValueAllowAll(_ reader: BufferReader) -> PrivacyRule? {
return Api.PrivacyRule.privacyValueAllowAll
}
public static func parse_privacyValueAllowChatParticipants(_ reader: BufferReader) -> PrivacyRule? {
var _1: [Int64]?
if let _ = reader.readInt32() {
_1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self)
}
let _c1 = _1 != nil
if _c1 {
return Api.PrivacyRule.privacyValueAllowChatParticipants(chats: _1!)
}
else {
return nil
}
}
public static func parse_privacyValueAllowCloseFriends(_ reader: BufferReader) -> PrivacyRule? {
return Api.PrivacyRule.privacyValueAllowCloseFriends
}
public static func parse_privacyValueAllowContacts(_ reader: BufferReader) -> PrivacyRule? {
return Api.PrivacyRule.privacyValueAllowContacts
}
public static func parse_privacyValueAllowUsers(_ reader: BufferReader) -> PrivacyRule? {
var _1: [Int64]?
if let _ = reader.readInt32() {
_1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self)
}
let _c1 = _1 != nil
if _c1 {
return Api.PrivacyRule.privacyValueAllowUsers(users: _1!)
}
else {
return nil
}
}
public static func parse_privacyValueDisallowAll(_ reader: BufferReader) -> PrivacyRule? {
return Api.PrivacyRule.privacyValueDisallowAll
}
public static func parse_privacyValueDisallowChatParticipants(_ reader: BufferReader) -> PrivacyRule? {
var _1: [Int64]?
if let _ = reader.readInt32() {
_1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self)
}
let _c1 = _1 != nil
if _c1 {
return Api.PrivacyRule.privacyValueDisallowChatParticipants(chats: _1!)
}
else {
return nil
}
}
public static func parse_privacyValueDisallowContacts(_ reader: BufferReader) -> PrivacyRule? {
return Api.PrivacyRule.privacyValueDisallowContacts
}
public static func parse_privacyValueDisallowUsers(_ reader: BufferReader) -> PrivacyRule? {
var _1: [Int64]?
if let _ = reader.readInt32() {
_1 = Api.parseVector(reader, elementSignature: 570911930, elementType: Int64.self)
}
let _c1 = _1 != nil
if _c1 {
return Api.PrivacyRule.privacyValueDisallowUsers(users: _1!)
}
else {
return nil
}
}
}
}
public extension Api { public extension Api {
enum Reaction: TypeConstructorDescription { enum Reaction: TypeConstructorDescription {
case reactionCustomEmoji(documentId: Int64) case reactionCustomEmoji(documentId: Int64)
@ -588,563 +768,3 @@ public extension Api {
} }
} }
public extension Api {
enum RequestPeerType: TypeConstructorDescription {
case requestPeerTypeBroadcast(flags: Int32, hasUsername: Api.Bool?, userAdminRights: Api.ChatAdminRights?, botAdminRights: Api.ChatAdminRights?)
case requestPeerTypeChat(flags: Int32, hasUsername: Api.Bool?, forum: Api.Bool?, userAdminRights: Api.ChatAdminRights?, botAdminRights: Api.ChatAdminRights?)
case requestPeerTypeUser(flags: Int32, bot: Api.Bool?, premium: Api.Bool?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .requestPeerTypeBroadcast(let flags, let hasUsername, let userAdminRights, let botAdminRights):
if boxed {
buffer.appendInt32(865857388)
}
serializeInt32(flags, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 3) != 0 {hasUsername!.serialize(buffer, true)}
if Int(flags) & Int(1 << 1) != 0 {userAdminRights!.serialize(buffer, true)}
if Int(flags) & Int(1 << 2) != 0 {botAdminRights!.serialize(buffer, true)}
break
case .requestPeerTypeChat(let flags, let hasUsername, let forum, let userAdminRights, let botAdminRights):
if boxed {
buffer.appendInt32(-906990053)
}
serializeInt32(flags, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 3) != 0 {hasUsername!.serialize(buffer, true)}
if Int(flags) & Int(1 << 4) != 0 {forum!.serialize(buffer, true)}
if Int(flags) & Int(1 << 1) != 0 {userAdminRights!.serialize(buffer, true)}
if Int(flags) & Int(1 << 2) != 0 {botAdminRights!.serialize(buffer, true)}
break
case .requestPeerTypeUser(let flags, let bot, let premium):
if boxed {
buffer.appendInt32(1597737472)
}
serializeInt32(flags, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {bot!.serialize(buffer, true)}
if Int(flags) & Int(1 << 1) != 0 {premium!.serialize(buffer, true)}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .requestPeerTypeBroadcast(let flags, let hasUsername, let userAdminRights, let botAdminRights):
return ("requestPeerTypeBroadcast", [("flags", flags as Any), ("hasUsername", hasUsername as Any), ("userAdminRights", userAdminRights as Any), ("botAdminRights", botAdminRights as Any)])
case .requestPeerTypeChat(let flags, let hasUsername, let forum, let userAdminRights, let botAdminRights):
return ("requestPeerTypeChat", [("flags", flags as Any), ("hasUsername", hasUsername as Any), ("forum", forum as Any), ("userAdminRights", userAdminRights as Any), ("botAdminRights", botAdminRights as Any)])
case .requestPeerTypeUser(let flags, let bot, let premium):
return ("requestPeerTypeUser", [("flags", flags as Any), ("bot", bot as Any), ("premium", premium as Any)])
}
}
public static func parse_requestPeerTypeBroadcast(_ reader: BufferReader) -> RequestPeerType? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Api.Bool?
if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.Bool
} }
var _3: Api.ChatAdminRights?
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
_3 = Api.parse(reader, signature: signature) as? Api.ChatAdminRights
} }
var _4: Api.ChatAdminRights?
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.ChatAdminRights
} }
let _c1 = _1 != nil
let _c2 = (Int(_1!) & Int(1 << 3) == 0) || _2 != nil
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.RequestPeerType.requestPeerTypeBroadcast(flags: _1!, hasUsername: _2, userAdminRights: _3, botAdminRights: _4)
}
else {
return nil
}
}
public static func parse_requestPeerTypeChat(_ reader: BufferReader) -> RequestPeerType? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Api.Bool?
if Int(_1!) & Int(1 << 3) != 0 {if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.Bool
} }
var _3: Api.Bool?
if Int(_1!) & Int(1 << 4) != 0 {if let signature = reader.readInt32() {
_3 = Api.parse(reader, signature: signature) as? Api.Bool
} }
var _4: Api.ChatAdminRights?
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.ChatAdminRights
} }
var _5: Api.ChatAdminRights?
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_5 = Api.parse(reader, signature: signature) as? Api.ChatAdminRights
} }
let _c1 = _1 != nil
let _c2 = (Int(_1!) & Int(1 << 3) == 0) || _2 != nil
let _c3 = (Int(_1!) & Int(1 << 4) == 0) || _3 != nil
let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil
let _c5 = (Int(_1!) & Int(1 << 2) == 0) || _5 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 {
return Api.RequestPeerType.requestPeerTypeChat(flags: _1!, hasUsername: _2, forum: _3, userAdminRights: _4, botAdminRights: _5)
}
else {
return nil
}
}
public static func parse_requestPeerTypeUser(_ reader: BufferReader) -> RequestPeerType? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Api.Bool?
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.Bool
} }
var _3: Api.Bool?
if Int(_1!) & Int(1 << 1) != 0 {if let signature = reader.readInt32() {
_3 = Api.parse(reader, signature: signature) as? Api.Bool
} }
let _c1 = _1 != nil
let _c2 = (Int(_1!) & Int(1 << 0) == 0) || _2 != nil
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
if _c1 && _c2 && _c3 {
return Api.RequestPeerType.requestPeerTypeUser(flags: _1!, bot: _2, premium: _3)
}
else {
return nil
}
}
}
}
public extension Api {
enum RestrictionReason: TypeConstructorDescription {
case restrictionReason(platform: String, reason: String, text: String)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .restrictionReason(let platform, let reason, let text):
if boxed {
buffer.appendInt32(-797791052)
}
serializeString(platform, buffer: buffer, boxed: false)
serializeString(reason, buffer: buffer, boxed: false)
serializeString(text, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .restrictionReason(let platform, let reason, let text):
return ("restrictionReason", [("platform", platform as Any), ("reason", reason as Any), ("text", text as Any)])
}
}
public static func parse_restrictionReason(_ reader: BufferReader) -> RestrictionReason? {
var _1: String?
_1 = parseString(reader)
var _2: String?
_2 = parseString(reader)
var _3: String?
_3 = parseString(reader)
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
if _c1 && _c2 && _c3 {
return Api.RestrictionReason.restrictionReason(platform: _1!, reason: _2!, text: _3!)
}
else {
return nil
}
}
}
}
public extension Api {
indirect enum RichText: TypeConstructorDescription {
case textAnchor(text: Api.RichText, name: String)
case textBold(text: Api.RichText)
case textConcat(texts: [Api.RichText])
case textEmail(text: Api.RichText, email: String)
case textEmpty
case textFixed(text: Api.RichText)
case textImage(documentId: Int64, w: Int32, h: Int32)
case textItalic(text: Api.RichText)
case textMarked(text: Api.RichText)
case textPhone(text: Api.RichText, phone: String)
case textPlain(text: String)
case textStrike(text: Api.RichText)
case textSubscript(text: Api.RichText)
case textSuperscript(text: Api.RichText)
case textUnderline(text: Api.RichText)
case textUrl(text: Api.RichText, url: String, webpageId: Int64)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .textAnchor(let text, let name):
if boxed {
buffer.appendInt32(894777186)
}
text.serialize(buffer, true)
serializeString(name, buffer: buffer, boxed: false)
break
case .textBold(let text):
if boxed {
buffer.appendInt32(1730456516)
}
text.serialize(buffer, true)
break
case .textConcat(let texts):
if boxed {
buffer.appendInt32(2120376535)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(texts.count))
for item in texts {
item.serialize(buffer, true)
}
break
case .textEmail(let text, let email):
if boxed {
buffer.appendInt32(-564523562)
}
text.serialize(buffer, true)
serializeString(email, buffer: buffer, boxed: false)
break
case .textEmpty:
if boxed {
buffer.appendInt32(-599948721)
}
break
case .textFixed(let text):
if boxed {
buffer.appendInt32(1816074681)
}
text.serialize(buffer, true)
break
case .textImage(let documentId, let w, let h):
if boxed {
buffer.appendInt32(136105807)
}
serializeInt64(documentId, buffer: buffer, boxed: false)
serializeInt32(w, buffer: buffer, boxed: false)
serializeInt32(h, buffer: buffer, boxed: false)
break
case .textItalic(let text):
if boxed {
buffer.appendInt32(-653089380)
}
text.serialize(buffer, true)
break
case .textMarked(let text):
if boxed {
buffer.appendInt32(55281185)
}
text.serialize(buffer, true)
break
case .textPhone(let text, let phone):
if boxed {
buffer.appendInt32(483104362)
}
text.serialize(buffer, true)
serializeString(phone, buffer: buffer, boxed: false)
break
case .textPlain(let text):
if boxed {
buffer.appendInt32(1950782688)
}
serializeString(text, buffer: buffer, boxed: false)
break
case .textStrike(let text):
if boxed {
buffer.appendInt32(-1678197867)
}
text.serialize(buffer, true)
break
case .textSubscript(let text):
if boxed {
buffer.appendInt32(-311786236)
}
text.serialize(buffer, true)
break
case .textSuperscript(let text):
if boxed {
buffer.appendInt32(-939827711)
}
text.serialize(buffer, true)
break
case .textUnderline(let text):
if boxed {
buffer.appendInt32(-1054465340)
}
text.serialize(buffer, true)
break
case .textUrl(let text, let url, let webpageId):
if boxed {
buffer.appendInt32(1009288385)
}
text.serialize(buffer, true)
serializeString(url, buffer: buffer, boxed: false)
serializeInt64(webpageId, buffer: buffer, boxed: false)
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .textAnchor(let text, let name):
return ("textAnchor", [("text", text as Any), ("name", name as Any)])
case .textBold(let text):
return ("textBold", [("text", text as Any)])
case .textConcat(let texts):
return ("textConcat", [("texts", texts as Any)])
case .textEmail(let text, let email):
return ("textEmail", [("text", text as Any), ("email", email as Any)])
case .textEmpty:
return ("textEmpty", [])
case .textFixed(let text):
return ("textFixed", [("text", text as Any)])
case .textImage(let documentId, let w, let h):
return ("textImage", [("documentId", documentId as Any), ("w", w as Any), ("h", h as Any)])
case .textItalic(let text):
return ("textItalic", [("text", text as Any)])
case .textMarked(let text):
return ("textMarked", [("text", text as Any)])
case .textPhone(let text, let phone):
return ("textPhone", [("text", text as Any), ("phone", phone as Any)])
case .textPlain(let text):
return ("textPlain", [("text", text as Any)])
case .textStrike(let text):
return ("textStrike", [("text", text as Any)])
case .textSubscript(let text):
return ("textSubscript", [("text", text as Any)])
case .textSuperscript(let text):
return ("textSuperscript", [("text", text as Any)])
case .textUnderline(let text):
return ("textUnderline", [("text", text as Any)])
case .textUrl(let text, let url, let webpageId):
return ("textUrl", [("text", text as Any), ("url", url as Any), ("webpageId", webpageId as Any)])
}
}
public static func parse_textAnchor(_ reader: BufferReader) -> RichText? {
var _1: Api.RichText?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.RichText
}
var _2: String?
_2 = parseString(reader)
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.RichText.textAnchor(text: _1!, name: _2!)
}
else {
return nil
}
}
public static func parse_textBold(_ reader: BufferReader) -> RichText? {
var _1: Api.RichText?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.RichText
}
let _c1 = _1 != nil
if _c1 {
return Api.RichText.textBold(text: _1!)
}
else {
return nil
}
}
public static func parse_textConcat(_ reader: BufferReader) -> RichText? {
var _1: [Api.RichText]?
if let _ = reader.readInt32() {
_1 = Api.parseVector(reader, elementSignature: 0, elementType: Api.RichText.self)
}
let _c1 = _1 != nil
if _c1 {
return Api.RichText.textConcat(texts: _1!)
}
else {
return nil
}
}
public static func parse_textEmail(_ reader: BufferReader) -> RichText? {
var _1: Api.RichText?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.RichText
}
var _2: String?
_2 = parseString(reader)
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.RichText.textEmail(text: _1!, email: _2!)
}
else {
return nil
}
}
public static func parse_textEmpty(_ reader: BufferReader) -> RichText? {
return Api.RichText.textEmpty
}
public static func parse_textFixed(_ reader: BufferReader) -> RichText? {
var _1: Api.RichText?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.RichText
}
let _c1 = _1 != nil
if _c1 {
return Api.RichText.textFixed(text: _1!)
}
else {
return nil
}
}
public static func parse_textImage(_ reader: BufferReader) -> RichText? {
var _1: Int64?
_1 = reader.readInt64()
var _2: Int32?
_2 = reader.readInt32()
var _3: Int32?
_3 = reader.readInt32()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
if _c1 && _c2 && _c3 {
return Api.RichText.textImage(documentId: _1!, w: _2!, h: _3!)
}
else {
return nil
}
}
public static func parse_textItalic(_ reader: BufferReader) -> RichText? {
var _1: Api.RichText?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.RichText
}
let _c1 = _1 != nil
if _c1 {
return Api.RichText.textItalic(text: _1!)
}
else {
return nil
}
}
public static func parse_textMarked(_ reader: BufferReader) -> RichText? {
var _1: Api.RichText?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.RichText
}
let _c1 = _1 != nil
if _c1 {
return Api.RichText.textMarked(text: _1!)
}
else {
return nil
}
}
public static func parse_textPhone(_ reader: BufferReader) -> RichText? {
var _1: Api.RichText?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.RichText
}
var _2: String?
_2 = parseString(reader)
let _c1 = _1 != nil
let _c2 = _2 != nil
if _c1 && _c2 {
return Api.RichText.textPhone(text: _1!, phone: _2!)
}
else {
return nil
}
}
public static func parse_textPlain(_ reader: BufferReader) -> RichText? {
var _1: String?
_1 = parseString(reader)
let _c1 = _1 != nil
if _c1 {
return Api.RichText.textPlain(text: _1!)
}
else {
return nil
}
}
public static func parse_textStrike(_ reader: BufferReader) -> RichText? {
var _1: Api.RichText?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.RichText
}
let _c1 = _1 != nil
if _c1 {
return Api.RichText.textStrike(text: _1!)
}
else {
return nil
}
}
public static func parse_textSubscript(_ reader: BufferReader) -> RichText? {
var _1: Api.RichText?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.RichText
}
let _c1 = _1 != nil
if _c1 {
return Api.RichText.textSubscript(text: _1!)
}
else {
return nil
}
}
public static func parse_textSuperscript(_ reader: BufferReader) -> RichText? {
var _1: Api.RichText?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.RichText
}
let _c1 = _1 != nil
if _c1 {
return Api.RichText.textSuperscript(text: _1!)
}
else {
return nil
}
}
public static func parse_textUnderline(_ reader: BufferReader) -> RichText? {
var _1: Api.RichText?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.RichText
}
let _c1 = _1 != nil
if _c1 {
return Api.RichText.textUnderline(text: _1!)
}
else {
return nil
}
}
public static func parse_textUrl(_ reader: BufferReader) -> RichText? {
var _1: Api.RichText?
if let signature = reader.readInt32() {
_1 = Api.parse(reader, signature: signature) as? Api.RichText
}
var _2: String?
_2 = parseString(reader)
var _3: Int64?
_3 = reader.readInt64()
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
if _c1 && _c2 && _c3 {
return Api.RichText.textUrl(text: _1!, url: _2!, webpageId: _3!)
}
else {
return nil
}
}
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -47,15 +47,15 @@ public struct MessageStatsContextState: Equatable {
public var stats: MessageStats? public var stats: MessageStats?
} }
private func requestMessageStats(postbox: Postbox, network: Network, datacenterId: Int32, messageId: MessageId, dark: Bool = false) -> Signal<MessageStats?, NoError> { private func requestMessageStats(postbox: Postbox, network: Network, messageId: MessageId, dark: Bool = false) -> Signal<MessageStats?, NoError> {
return postbox.transaction { transaction -> (Peer, Message)? in return postbox.transaction { transaction -> (Int32, Peer, Message)? in
if let peer = transaction.getPeer(messageId.peerId), let message = transaction.getMessage(messageId) { if let peer = transaction.getPeer(messageId.peerId), let message = transaction.getMessage(messageId), let cachedData = transaction.getPeerCachedData(peerId: messageId.peerId) as? CachedChannelData {
return (peer, message) return (cachedData.statsDatacenterId, peer, message)
} else { } else {
return nil return nil
} }
} |> mapToSignal { peerAndMessage -> Signal<MessageStats?, NoError> in } |> mapToSignal { data -> Signal<MessageStats?, NoError> in
guard let (peer, message) = peerAndMessage, let inputChannel = apiInputChannel(peer) else { guard let (datacenterId, peer, message) = data, let inputChannel = apiInputChannel(peer) else {
return .never() return .never()
} }
@ -125,7 +125,6 @@ private func requestMessageStats(postbox: Postbox, network: Network, datacenterI
private final class MessageStatsContextImpl { private final class MessageStatsContextImpl {
private let postbox: Postbox private let postbox: Postbox
private let network: Network private let network: Network
private let datacenterId: Int32
private let messageId: MessageId private let messageId: MessageId
private var _state: MessageStatsContextState { private var _state: MessageStatsContextState {
@ -143,12 +142,11 @@ private final class MessageStatsContextImpl {
private let disposable = MetaDisposable() private let disposable = MetaDisposable()
private let disposables = DisposableDict<String>() private let disposables = DisposableDict<String>()
init(postbox: Postbox, network: Network, datacenterId: Int32, messageId: MessageId) { init(postbox: Postbox, network: Network, messageId: MessageId) {
assert(Queue.mainQueue().isCurrent()) assert(Queue.mainQueue().isCurrent())
self.postbox = postbox self.postbox = postbox
self.network = network self.network = network
self.datacenterId = datacenterId
self.messageId = messageId self.messageId = messageId
self._state = MessageStatsContextState(stats: nil) self._state = MessageStatsContextState(stats: nil)
self._statePromise.set(.single(self._state)) self._statePromise.set(.single(self._state))
@ -165,7 +163,7 @@ private final class MessageStatsContextImpl {
private func load() { private func load() {
assert(Queue.mainQueue().isCurrent()) assert(Queue.mainQueue().isCurrent())
self.disposable.set((requestMessageStats(postbox: self.postbox, network: self.network, datacenterId: self.datacenterId, messageId: self.messageId) self.disposable.set((requestMessageStats(postbox: self.postbox, network: self.network, messageId: self.messageId)
|> deliverOnMainQueue).start(next: { [weak self] stats in |> deliverOnMainQueue).start(next: { [weak self] stats in
if let strongSelf = self { if let strongSelf = self {
strongSelf._state = MessageStatsContextState(stats: stats) strongSelf._state = MessageStatsContextState(stats: stats)
@ -176,7 +174,7 @@ private final class MessageStatsContextImpl {
func loadDetailedGraph(_ graph: StatsGraph, x: Int64) -> Signal<StatsGraph?, NoError> { func loadDetailedGraph(_ graph: StatsGraph, x: Int64) -> Signal<StatsGraph?, NoError> {
if let token = graph.token { if let token = graph.token {
return requestGraph(network: self.network, datacenterId: self.datacenterId, token: token, x: x) return requestGraph(postbox: self.postbox, network: self.network, peerId: self.messageId.peerId, token: token, x: x)
} else { } else {
return .single(nil) return .single(nil)
} }
@ -198,9 +196,9 @@ public final class MessageStatsContext {
} }
} }
public init(postbox: Postbox, network: Network, datacenterId: Int32, messageId: MessageId) { public init(postbox: Postbox, network: Network, messageId: MessageId) {
self.impl = QueueLocalObject(queue: Queue.mainQueue(), generate: { self.impl = QueueLocalObject(queue: Queue.mainQueue(), generate: {
return MessageStatsContextImpl(postbox: postbox, network: network, datacenterId: datacenterId, messageId: messageId) return MessageStatsContextImpl(postbox: postbox, network: network, messageId: messageId)
}) })
} }

View File

@ -48,25 +48,34 @@ public enum StatsGraph: Equatable {
} }
} }
public struct ChannelStatsMessageInteractions: Equatable { public struct ChannelStatsPostInteractions: Equatable {
public let messageId: MessageId public enum PostId: Hashable {
case message(id: EngineMessage.Id)
case story(peerId: EnginePeer.Id, id: Int32)
}
public let postId: PostId
public let views: Int32 public let views: Int32
public let forwards: Int32 public let forwards: Int32
public let reactions: Int32 public let reactions: Int32
public init(messageId: MessageId, views: Int32, forwards: Int32, reactions: Int32) { public init(postId: PostId, views: Int32, forwards: Int32, reactions: Int32) {
self.messageId = messageId self.postId = postId
self.views = views self.views = views
self.forwards = forwards self.forwards = forwards
self.reactions = reactions self.reactions = reactions
} }
} }
public final class ChannelStats: Equatable { public struct ChannelStats: Equatable {
public let period: StatsDateRange public let period: StatsDateRange
public let followers: StatsValue public let followers: StatsValue
public let viewsPerPost: StatsValue public let viewsPerPost: StatsValue
public let sharesPerPost: StatsValue public let sharesPerPost: StatsValue
public let reactionsPerPost: StatsValue
public let viewsPerStory: StatsValue
public let sharesPerStory: StatsValue
public let reactionsPerStory: StatsValue
public let enabledNotifications: StatsPercentValue public let enabledNotifications: StatsPercentValue
public let growthGraph: StatsGraph public let growthGraph: StatsGraph
public let followersGraph: StatsGraph public let followersGraph: StatsGraph
@ -80,13 +89,17 @@ public final class ChannelStats: Equatable {
public let reactionsByEmotionGraph: StatsGraph public let reactionsByEmotionGraph: StatsGraph
public let storyInteractionsGraph: StatsGraph public let storyInteractionsGraph: StatsGraph
public let storyReactionsByEmotionGraph: StatsGraph public let storyReactionsByEmotionGraph: StatsGraph
public let messageInteractions: [ChannelStatsMessageInteractions] public let postInteractions: [ChannelStatsPostInteractions]
init( init(
period: StatsDateRange, period: StatsDateRange,
followers: StatsValue, followers: StatsValue,
viewsPerPost: StatsValue, viewsPerPost: StatsValue,
sharesPerPost: StatsValue, sharesPerPost: StatsValue,
reactionsPerPost: StatsValue,
viewsPerStory: StatsValue,
sharesPerStory: StatsValue,
reactionsPerStory: StatsValue,
enabledNotifications: StatsPercentValue, enabledNotifications: StatsPercentValue,
growthGraph: StatsGraph, growthGraph: StatsGraph,
followersGraph: StatsGraph, followersGraph: StatsGraph,
@ -100,12 +113,16 @@ public final class ChannelStats: Equatable {
reactionsByEmotionGraph: StatsGraph, reactionsByEmotionGraph: StatsGraph,
storyInteractionsGraph: StatsGraph, storyInteractionsGraph: StatsGraph,
storyReactionsByEmotionGraph: StatsGraph, storyReactionsByEmotionGraph: StatsGraph,
messageInteractions: [ChannelStatsMessageInteractions] postInteractions: [ChannelStatsPostInteractions]
) { ) {
self.period = period self.period = period
self.followers = followers self.followers = followers
self.viewsPerPost = viewsPerPost self.viewsPerPost = viewsPerPost
self.sharesPerPost = sharesPerPost self.sharesPerPost = sharesPerPost
self.reactionsPerPost = reactionsPerPost
self.viewsPerStory = viewsPerStory
self.sharesPerStory = sharesPerStory
self.reactionsPerStory = reactionsPerStory
self.enabledNotifications = enabledNotifications self.enabledNotifications = enabledNotifications
self.growthGraph = growthGraph self.growthGraph = growthGraph
self.followersGraph = followersGraph self.followersGraph = followersGraph
@ -119,7 +136,7 @@ public final class ChannelStats: Equatable {
self.reactionsByEmotionGraph = reactionsByEmotionGraph self.reactionsByEmotionGraph = reactionsByEmotionGraph
self.storyInteractionsGraph = storyInteractionsGraph self.storyInteractionsGraph = storyInteractionsGraph
self.storyReactionsByEmotionGraph = storyReactionsByEmotionGraph self.storyReactionsByEmotionGraph = storyReactionsByEmotionGraph
self.messageInteractions = messageInteractions self.postInteractions = postInteractions
} }
public static func == (lhs: ChannelStats, rhs: ChannelStats) -> Bool { public static func == (lhs: ChannelStats, rhs: ChannelStats) -> Bool {
@ -174,56 +191,56 @@ public final class ChannelStats: Equatable {
if lhs.storyReactionsByEmotionGraph != rhs.storyReactionsByEmotionGraph { if lhs.storyReactionsByEmotionGraph != rhs.storyReactionsByEmotionGraph {
return false return false
} }
if lhs.messageInteractions != rhs.messageInteractions { if lhs.postInteractions != rhs.postInteractions {
return false return false
} }
return true return true
} }
public func withUpdatedGrowthGraph(_ growthGraph: StatsGraph) -> ChannelStats { public func withUpdatedGrowthGraph(_ growthGraph: StatsGraph) -> ChannelStats {
return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, messageInteractions: self.messageInteractions) return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, reactionsPerPost: self.reactionsPerPost, viewsPerStory: self.viewsPerStory, sharesPerStory: self.sharesPerStory, reactionsPerStory: self.reactionsPerStory, enabledNotifications: self.enabledNotifications, growthGraph: growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, postInteractions: self.postInteractions)
} }
public func withUpdatedFollowersGraph(_ followersGraph: StatsGraph) -> ChannelStats { public func withUpdatedFollowersGraph(_ followersGraph: StatsGraph) -> ChannelStats {
return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, messageInteractions: self.messageInteractions) return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, reactionsPerPost: self.reactionsPerPost, viewsPerStory: self.viewsPerStory, sharesPerStory: self.sharesPerStory, reactionsPerStory: self.reactionsPerStory, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, postInteractions: self.postInteractions)
} }
public func withUpdatedMuteGraph(_ muteGraph: StatsGraph) -> ChannelStats { public func withUpdatedMuteGraph(_ muteGraph: StatsGraph) -> ChannelStats {
return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, messageInteractions: self.messageInteractions) return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, reactionsPerPost: self.reactionsPerPost, viewsPerStory: self.viewsPerStory, sharesPerStory: self.sharesPerStory, reactionsPerStory: self.reactionsPerStory, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, postInteractions: self.postInteractions)
} }
public func withUpdatedTopHoursGraph(_ viewsByHourGraph: StatsGraph) -> ChannelStats { public func withUpdatedTopHoursGraph(_ viewsByHourGraph: StatsGraph) -> ChannelStats {
return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: viewsByHourGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, messageInteractions: self.messageInteractions) return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, reactionsPerPost: self.reactionsPerPost, viewsPerStory: self.viewsPerStory, sharesPerStory: self.sharesPerStory, reactionsPerStory: self.reactionsPerStory, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: viewsByHourGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, postInteractions: self.postInteractions)
} }
public func withUpdatedInteractionsGraph(_ interactionsGraph: StatsGraph) -> ChannelStats { public func withUpdatedInteractionsGraph(_ interactionsGraph: StatsGraph) -> ChannelStats {
return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, messageInteractions: self.messageInteractions) return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, reactionsPerPost: self.reactionsPerPost, viewsPerStory: self.viewsPerStory, sharesPerStory: self.sharesPerStory, reactionsPerStory: self.reactionsPerStory, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, postInteractions: self.postInteractions)
} }
public func withUpdatedInstantPageInteractionsGraph(_ instantPageInteractionsGraph: StatsGraph) -> ChannelStats { public func withUpdatedInstantPageInteractionsGraph(_ instantPageInteractionsGraph: StatsGraph) -> ChannelStats {
return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, messageInteractions: self.messageInteractions) return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, reactionsPerPost: self.reactionsPerPost, viewsPerStory: self.viewsPerStory, sharesPerStory: self.sharesPerStory, reactionsPerStory: self.reactionsPerStory, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, postInteractions: self.postInteractions)
} }
public func withUpdatedViewsBySourceGraph(_ viewsBySourceGraph: StatsGraph) -> ChannelStats { public func withUpdatedViewsBySourceGraph(_ viewsBySourceGraph: StatsGraph) -> ChannelStats {
return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, messageInteractions: self.messageInteractions) return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, reactionsPerPost: self.reactionsPerPost, viewsPerStory: self.viewsPerStory, sharesPerStory: self.sharesPerStory, reactionsPerStory: self.reactionsPerStory, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, postInteractions: self.postInteractions)
} }
public func withUpdatedNewFollowersBySourceGraph(_ newFollowersBySourceGraph: StatsGraph) -> ChannelStats { public func withUpdatedNewFollowersBySourceGraph(_ newFollowersBySourceGraph: StatsGraph) -> ChannelStats {
return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, messageInteractions: self.messageInteractions) return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, reactionsPerPost: self.reactionsPerPost, viewsPerStory: self.viewsPerStory, sharesPerStory: self.sharesPerStory, reactionsPerStory: self.reactionsPerStory, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, postInteractions: self.postInteractions)
} }
public func withUpdatedLanguagesGraph(_ languagesGraph: StatsGraph) -> ChannelStats { public func withUpdatedLanguagesGraph(_ languagesGraph: StatsGraph) -> ChannelStats {
return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, messageInteractions: self.messageInteractions) return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, reactionsPerPost: self.reactionsPerPost, viewsPerStory: self.viewsPerStory, sharesPerStory: self.sharesPerStory, reactionsPerStory: self.reactionsPerStory, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, postInteractions: self.postInteractions)
} }
public func withUpdatedReactionsByEmotionGraph(_ reactionsByEmotionGraph: StatsGraph) -> ChannelStats { public func withUpdatedReactionsByEmotionGraph(_ reactionsByEmotionGraph: StatsGraph) -> ChannelStats {
return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, messageInteractions: self.messageInteractions) return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, reactionsPerPost: self.reactionsPerPost, viewsPerStory: self.viewsPerStory, sharesPerStory: self.sharesPerStory, reactionsPerStory: self.reactionsPerStory, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, postInteractions: self.postInteractions)
} }
public func withUpdatedStoryInteractionsGraph(_ storyInteractionsGraph: StatsGraph) -> ChannelStats { public func withUpdatedStoryInteractionsGraph(_ storyInteractionsGraph: StatsGraph) -> ChannelStats {
return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, messageInteractions: self.messageInteractions) return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, reactionsPerPost: self.reactionsPerPost, viewsPerStory: self.viewsPerStory, sharesPerStory: self.sharesPerStory, reactionsPerStory: self.reactionsPerStory, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: storyInteractionsGraph, storyReactionsByEmotionGraph: self.storyReactionsByEmotionGraph, postInteractions: self.postInteractions)
} }
public func withUpdatedStoryReactionsByEmotionGraph(_ storyReactionsByEmotionGraph: StatsGraph) -> ChannelStats { public func withUpdatedStoryReactionsByEmotionGraph(_ storyReactionsByEmotionGraph: StatsGraph) -> ChannelStats {
return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: storyReactionsByEmotionGraph, messageInteractions: self.messageInteractions) return ChannelStats(period: self.period, followers: self.followers, viewsPerPost: self.viewsPerPost, sharesPerPost: self.sharesPerPost, reactionsPerPost: self.reactionsPerPost, viewsPerStory: self.viewsPerStory, sharesPerStory: self.sharesPerStory, reactionsPerStory: self.reactionsPerStory, enabledNotifications: self.enabledNotifications, growthGraph: self.growthGraph, followersGraph: self.followersGraph, muteGraph: self.muteGraph, topHoursGraph: self.topHoursGraph, interactionsGraph: self.interactionsGraph, instantPageInteractionsGraph: self.instantPageInteractionsGraph, viewsBySourceGraph: self.viewsBySourceGraph, newFollowersBySourceGraph: self.newFollowersBySourceGraph, languagesGraph: self.languagesGraph, reactionsByEmotionGraph: self.reactionsByEmotionGraph, storyInteractionsGraph: self.storyInteractionsGraph, storyReactionsByEmotionGraph: storyReactionsByEmotionGraph, postInteractions: self.postInteractions)
} }
} }
@ -231,11 +248,14 @@ public struct ChannelStatsContextState: Equatable {
public var stats: ChannelStats? public var stats: ChannelStats?
} }
private func requestChannelStats(postbox: Postbox, network: Network, datacenterId: Int32, peerId: PeerId, dark: Bool = false) -> Signal<ChannelStats?, NoError> { private func requestChannelStats(postbox: Postbox, network: Network, peerId: PeerId, dark: Bool = false) -> Signal<ChannelStats?, NoError> {
return postbox.transaction { transaction -> Peer? in return postbox.transaction { transaction -> (Int32, Peer)? in
return transaction.getPeer(peerId) if let peer = transaction.getPeer(peerId), let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData {
} |> mapToSignal { peer -> Signal<ChannelStats?, NoError> in return (cachedData.statsDatacenterId, peer)
guard let peer = peer, let inputChannel = apiInputChannel(peer) else { }
return nil
} |> mapToSignal { data -> Signal<ChannelStats?, NoError> in
guard let (statsDatacenterId, peer) = data, let inputChannel = apiInputChannel(peer) else {
return .never() return .never()
} }
@ -246,8 +266,8 @@ private func requestChannelStats(postbox: Postbox, network: Network, datacenterI
let request = Api.functions.stats.getBroadcastStats(flags: flags, channel: inputChannel) let request = Api.functions.stats.getBroadcastStats(flags: flags, channel: inputChannel)
let signal: Signal<Api.stats.BroadcastStats, MTRpcError> let signal: Signal<Api.stats.BroadcastStats, MTRpcError>
if network.datacenterId != datacenterId { if network.datacenterId != statsDatacenterId {
signal = network.download(datacenterId: Int(datacenterId), isMedia: false, tag: nil) signal = network.download(datacenterId: Int(statsDatacenterId), isMedia: false, tag: nil)
|> castError(MTRpcError.self) |> castError(MTRpcError.self)
|> mapToSignal { worker in |> mapToSignal { worker in
return worker.request(request) return worker.request(request)
@ -264,20 +284,33 @@ private func requestChannelStats(postbox: Postbox, network: Network, datacenterI
} }
} }
func requestGraph(network: Network, datacenterId: Int32, token: String, x: Int64? = nil) -> Signal<StatsGraph?, NoError> { func requestGraph(postbox: Postbox, network: Network, peerId: EnginePeer.Id, token: String, x: Int64? = nil) -> Signal<StatsGraph?, NoError> {
var flags: Int32 = 0 var flags: Int32 = 0
if let _ = x { if let _ = x {
flags |= (1 << 0) flags |= (1 << 0)
} }
let signal: Signal<Api.StatsGraph, MTRpcError> let signal: Signal<Api.StatsGraph, MTRpcError>
if network.datacenterId != datacenterId { signal = postbox.transaction { transaction -> Int32? in
signal = network.download(datacenterId: Int(datacenterId), isMedia: false, tag: nil) if let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData {
|> castError(MTRpcError.self) return cachedData.statsDatacenterId
|> mapToSignal { worker in }
return worker.request(Api.functions.stats.loadAsyncGraph(flags: flags, token: token, x: x)) return nil
}
|> castError(MTRpcError.self)
|> mapToSignal { statsDatacenterId in
if let statsDatacenterId = statsDatacenterId {
if network.datacenterId != statsDatacenterId {
return network.download(datacenterId: Int(statsDatacenterId), isMedia: false, tag: nil)
|> castError(MTRpcError.self)
|> mapToSignal { worker in
return worker.request(Api.functions.stats.loadAsyncGraph(flags: flags, token: token, x: x))
}
} else {
return network.request(Api.functions.stats.loadAsyncGraph(flags: flags, token: token, x: x))
}
} else {
return .complete()
} }
} else {
signal = network.request(Api.functions.stats.loadAsyncGraph(flags: flags, token: token, x: x))
} }
return signal return signal
@ -292,7 +325,6 @@ func requestGraph(network: Network, datacenterId: Int32, token: String, x: Int64
private final class ChannelStatsContextImpl { private final class ChannelStatsContextImpl {
private let postbox: Postbox private let postbox: Postbox
private let network: Network private let network: Network
private let datacenterId: Int32
private let peerId: PeerId private let peerId: PeerId
private var _state: ChannelStatsContextState { private var _state: ChannelStatsContextState {
@ -310,12 +342,11 @@ private final class ChannelStatsContextImpl {
private let disposable = MetaDisposable() private let disposable = MetaDisposable()
private let disposables = DisposableDict<String>() private let disposables = DisposableDict<String>()
init(postbox: Postbox, network: Network, datacenterId: Int32, peerId: PeerId) { init(postbox: Postbox, network: Network, peerId: PeerId) {
assert(Queue.mainQueue().isCurrent()) assert(Queue.mainQueue().isCurrent())
self.postbox = postbox self.postbox = postbox
self.network = network self.network = network
self.datacenterId = datacenterId
self.peerId = peerId self.peerId = peerId
self._state = ChannelStatsContextState(stats: nil) self._state = ChannelStatsContextState(stats: nil)
self._statePromise.set(.single(self._state)) self._statePromise.set(.single(self._state))
@ -332,7 +363,7 @@ private final class ChannelStatsContextImpl {
private func load() { private func load() {
assert(Queue.mainQueue().isCurrent()) assert(Queue.mainQueue().isCurrent())
self.disposable.set((requestChannelStats(postbox: self.postbox, network: self.network, datacenterId: self.datacenterId, peerId: self.peerId) self.disposable.set((requestChannelStats(postbox: self.postbox, network: self.network, peerId: self.peerId)
|> deliverOnMainQueue).start(next: { [weak self] stats in |> deliverOnMainQueue).start(next: { [weak self] stats in
if let strongSelf = self { if let strongSelf = self {
strongSelf._state = ChannelStatsContextState(stats: stats) strongSelf._state = ChannelStatsContextState(stats: stats)
@ -346,7 +377,7 @@ private final class ChannelStatsContextImpl {
return return
} }
if case let .OnDemand(token) = stats.growthGraph { if case let .OnDemand(token) = stats.growthGraph {
self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token)
|> deliverOnMainQueue).start(next: { [weak self] graph in |> deliverOnMainQueue).start(next: { [weak self] graph in
if let strongSelf = self, let graph = graph { if let strongSelf = self, let graph = graph {
strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedGrowthGraph(graph)) strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedGrowthGraph(graph))
@ -361,7 +392,7 @@ private final class ChannelStatsContextImpl {
return return
} }
if case let .OnDemand(token) = stats.followersGraph { if case let .OnDemand(token) = stats.followersGraph {
self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token)
|> deliverOnMainQueue).start(next: { [weak self] graph in |> deliverOnMainQueue).start(next: { [weak self] graph in
if let strongSelf = self, let graph = graph { if let strongSelf = self, let graph = graph {
strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedFollowersGraph(graph)) strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedFollowersGraph(graph))
@ -376,7 +407,7 @@ private final class ChannelStatsContextImpl {
return return
} }
if case let .OnDemand(token) = stats.muteGraph { if case let .OnDemand(token) = stats.muteGraph {
self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token)
|> deliverOnMainQueue).start(next: { [weak self] graph in |> deliverOnMainQueue).start(next: { [weak self] graph in
if let strongSelf = self, let graph = graph { if let strongSelf = self, let graph = graph {
strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedMuteGraph(graph)) strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedMuteGraph(graph))
@ -391,7 +422,7 @@ private final class ChannelStatsContextImpl {
return return
} }
if case let .OnDemand(token) = stats.topHoursGraph { if case let .OnDemand(token) = stats.topHoursGraph {
self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token)
|> deliverOnMainQueue).start(next: { [weak self] graph in |> deliverOnMainQueue).start(next: { [weak self] graph in
if let strongSelf = self, let graph = graph { if let strongSelf = self, let graph = graph {
strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedTopHoursGraph(graph)) strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedTopHoursGraph(graph))
@ -406,7 +437,7 @@ private final class ChannelStatsContextImpl {
return return
} }
if case let .OnDemand(token) = stats.interactionsGraph { if case let .OnDemand(token) = stats.interactionsGraph {
self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token)
|> deliverOnMainQueue).start(next: { [weak self] graph in |> deliverOnMainQueue).start(next: { [weak self] graph in
if let strongSelf = self, let graph = graph { if let strongSelf = self, let graph = graph {
strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedInteractionsGraph(graph)) strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedInteractionsGraph(graph))
@ -421,7 +452,7 @@ private final class ChannelStatsContextImpl {
return return
} }
if case let .OnDemand(token) = stats.instantPageInteractionsGraph { if case let .OnDemand(token) = stats.instantPageInteractionsGraph {
self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token)
|> deliverOnMainQueue).start(next: { [weak self] graph in |> deliverOnMainQueue).start(next: { [weak self] graph in
if let strongSelf = self, let graph = graph { if let strongSelf = self, let graph = graph {
strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedInstantPageInteractionsGraph(graph)) strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedInstantPageInteractionsGraph(graph))
@ -436,7 +467,7 @@ private final class ChannelStatsContextImpl {
return return
} }
if case let .OnDemand(token) = stats.viewsBySourceGraph { if case let .OnDemand(token) = stats.viewsBySourceGraph {
self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token)
|> deliverOnMainQueue).start(next: { [weak self] graph in |> deliverOnMainQueue).start(next: { [weak self] graph in
if let strongSelf = self, let graph = graph { if let strongSelf = self, let graph = graph {
strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedViewsBySourceGraph(graph)) strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedViewsBySourceGraph(graph))
@ -451,7 +482,7 @@ private final class ChannelStatsContextImpl {
return return
} }
if case let .OnDemand(token) = stats.newFollowersBySourceGraph { if case let .OnDemand(token) = stats.newFollowersBySourceGraph {
self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token)
|> deliverOnMainQueue).start(next: { [weak self] graph in |> deliverOnMainQueue).start(next: { [weak self] graph in
if let strongSelf = self, let graph = graph { if let strongSelf = self, let graph = graph {
strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedNewFollowersBySourceGraph(graph)) strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedNewFollowersBySourceGraph(graph))
@ -466,7 +497,7 @@ private final class ChannelStatsContextImpl {
return return
} }
if case let .OnDemand(token) = stats.languagesGraph { if case let .OnDemand(token) = stats.languagesGraph {
self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token)
|> deliverOnMainQueue).start(next: { [weak self] graph in |> deliverOnMainQueue).start(next: { [weak self] graph in
if let strongSelf = self, let graph = graph { if let strongSelf = self, let graph = graph {
strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedLanguagesGraph(graph)) strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedLanguagesGraph(graph))
@ -481,7 +512,7 @@ private final class ChannelStatsContextImpl {
return return
} }
if case let .OnDemand(token) = stats.reactionsByEmotionGraph { if case let .OnDemand(token) = stats.reactionsByEmotionGraph {
self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token)
|> deliverOnMainQueue).start(next: { [weak self] graph in |> deliverOnMainQueue).start(next: { [weak self] graph in
if let strongSelf = self, let graph = graph { if let strongSelf = self, let graph = graph {
strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedReactionsByEmotionGraph(graph)) strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedReactionsByEmotionGraph(graph))
@ -496,7 +527,7 @@ private final class ChannelStatsContextImpl {
return return
} }
if case let .OnDemand(token) = stats.storyInteractionsGraph { if case let .OnDemand(token) = stats.storyInteractionsGraph {
self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token)
|> deliverOnMainQueue).start(next: { [weak self] graph in |> deliverOnMainQueue).start(next: { [weak self] graph in
if let strongSelf = self, let graph = graph { if let strongSelf = self, let graph = graph {
strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedStoryInteractionsGraph(graph)) strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedStoryInteractionsGraph(graph))
@ -511,7 +542,7 @@ private final class ChannelStatsContextImpl {
return return
} }
if case let .OnDemand(token) = stats.storyReactionsByEmotionGraph { if case let .OnDemand(token) = stats.storyReactionsByEmotionGraph {
self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token)
|> deliverOnMainQueue).start(next: { [weak self] graph in |> deliverOnMainQueue).start(next: { [weak self] graph in
if let strongSelf = self, let graph = graph { if let strongSelf = self, let graph = graph {
strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedStoryReactionsByEmotionGraph(graph)) strongSelf._state = ChannelStatsContextState(stats: strongSelf._state.stats?.withUpdatedStoryReactionsByEmotionGraph(graph))
@ -523,7 +554,7 @@ private final class ChannelStatsContextImpl {
func loadDetailedGraph(_ graph: StatsGraph, x: Int64) -> Signal<StatsGraph?, NoError> { func loadDetailedGraph(_ graph: StatsGraph, x: Int64) -> Signal<StatsGraph?, NoError> {
if let token = graph.token { if let token = graph.token {
return requestGraph(network: self.network, datacenterId: self.datacenterId, token: token, x: x) return requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token, x: x)
} else { } else {
return .single(nil) return .single(nil)
} }
@ -545,9 +576,9 @@ public final class ChannelStatsContext {
} }
} }
public init(postbox: Postbox, network: Network, datacenterId: Int32, peerId: PeerId) { public init(postbox: Postbox, network: Network, peerId: PeerId) {
self.impl = QueueLocalObject(queue: Queue.mainQueue(), generate: { self.impl = QueueLocalObject(queue: Queue.mainQueue(), generate: {
return ChannelStatsContextImpl(postbox: postbox, network: network, datacenterId: datacenterId, peerId: peerId) return ChannelStatsContextImpl(postbox: postbox, network: network, peerId: peerId)
}) })
} }
@ -652,7 +683,7 @@ public struct GroupStatsTopInviter: Equatable {
public let inviteCount: Int32 public let inviteCount: Int32
} }
public final class GroupStats: Equatable { public struct GroupStats: Equatable {
public let period: StatsDateRange public let period: StatsDateRange
public let members: StatsValue public let members: StatsValue
public let messages: StatsValue public let messages: StatsValue
@ -689,58 +720,6 @@ public final class GroupStats: Equatable {
self.topInviters = topInviters self.topInviters = topInviters
} }
public static func == (lhs: GroupStats, rhs: GroupStats) -> Bool {
if lhs.period != rhs.period {
return false
}
if lhs.members != rhs.members {
return false
}
if lhs.messages != rhs.messages {
return false
}
if lhs.viewers != rhs.viewers {
return false
}
if lhs.posters != rhs.posters {
return false
}
if lhs.growthGraph != rhs.growthGraph {
return false
}
if lhs.membersGraph != rhs.membersGraph {
return false
}
if lhs.newMembersBySourceGraph != rhs.newMembersBySourceGraph {
return false
}
if lhs.languagesGraph != rhs.languagesGraph {
return false
}
if lhs.messagesGraph != rhs.messagesGraph {
return false
}
if lhs.actionsGraph != rhs.actionsGraph {
return false
}
if lhs.topHoursGraph != rhs.topHoursGraph {
return false
}
if lhs.topWeekdaysGraph != rhs.topWeekdaysGraph {
return false
}
if lhs.topPosters != rhs.topPosters {
return false
}
if lhs.topAdmins != rhs.topAdmins {
return false
}
if lhs.topInviters != rhs.topInviters {
return false
}
return true
}
public func withUpdatedGrowthGraph(_ growthGraph: StatsGraph) -> GroupStats { 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, topWeekdaysGraph: self.topWeekdaysGraph, 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)
} }
@ -778,11 +757,14 @@ public struct GroupStatsContextState: Equatable {
public var stats: GroupStats? public var stats: GroupStats?
} }
private func requestGroupStats(accountPeerId: PeerId, postbox: Postbox, network: Network, datacenterId: Int32, peerId: PeerId, dark: Bool = false) -> Signal<GroupStats?, NoError> { private func requestGroupStats(accountPeerId: PeerId, postbox: Postbox, network: Network, peerId: PeerId, dark: Bool = false) -> Signal<GroupStats?, NoError> {
return postbox.transaction { transaction -> Peer? in return postbox.transaction { transaction -> (Int32, Peer)? in
return transaction.getPeer(peerId) if let peer = transaction.getPeer(peerId), let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData {
} |> mapToSignal { peer -> Signal<GroupStats?, NoError> in return (cachedData.statsDatacenterId, peer)
guard let peer = peer, let inputChannel = apiInputChannel(peer) else { }
return nil
} |> mapToSignal { data -> Signal<GroupStats?, NoError> in
guard let (statsDatacenterId, peer) = data, let inputChannel = apiInputChannel(peer) else {
return .never() return .never()
} }
@ -792,8 +774,8 @@ private func requestGroupStats(accountPeerId: PeerId, postbox: Postbox, network:
} }
let signal: Signal<Api.stats.MegagroupStats, MTRpcError> let signal: Signal<Api.stats.MegagroupStats, MTRpcError>
if network.datacenterId != datacenterId { if network.datacenterId != statsDatacenterId {
signal = network.download(datacenterId: Int(datacenterId), isMedia: false, tag: nil) signal = network.download(datacenterId: Int(statsDatacenterId), isMedia: false, tag: nil)
|> castError(MTRpcError.self) |> castError(MTRpcError.self)
|> mapToSignal { worker in |> mapToSignal { worker in
return worker.request(Api.functions.stats.getMegagroupStats(flags: flags, channel: inputChannel)) return worker.request(Api.functions.stats.getMegagroupStats(flags: flags, channel: inputChannel))
@ -820,7 +802,6 @@ private final class GroupStatsContextImpl {
private let postbox: Postbox private let postbox: Postbox
private let network: Network private let network: Network
private let accountPeerId: PeerId private let accountPeerId: PeerId
private let datacenterId: Int32
private let peerId: PeerId private let peerId: PeerId
private var _state: GroupStatsContextState { private var _state: GroupStatsContextState {
@ -838,13 +819,12 @@ private final class GroupStatsContextImpl {
private let disposable = MetaDisposable() private let disposable = MetaDisposable()
private let disposables = DisposableDict<String>() private let disposables = DisposableDict<String>()
init(postbox: Postbox, network: Network, accountPeerId: PeerId, datacenterId: Int32, peerId: PeerId) { init(postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId) {
assert(Queue.mainQueue().isCurrent()) assert(Queue.mainQueue().isCurrent())
self.postbox = postbox self.postbox = postbox
self.network = network self.network = network
self.accountPeerId = accountPeerId self.accountPeerId = accountPeerId
self.datacenterId = datacenterId
self.peerId = peerId self.peerId = peerId
self._state = GroupStatsContextState(stats: nil) self._state = GroupStatsContextState(stats: nil)
self._statePromise.set(.single(self._state)) self._statePromise.set(.single(self._state))
@ -861,7 +841,7 @@ private final class GroupStatsContextImpl {
private func load() { private func load() {
assert(Queue.mainQueue().isCurrent()) assert(Queue.mainQueue().isCurrent())
self.disposable.set((requestGroupStats(accountPeerId: self.accountPeerId, postbox: self.postbox, network: self.network, datacenterId: self.datacenterId, peerId: self.peerId) self.disposable.set((requestGroupStats(accountPeerId: self.accountPeerId, postbox: self.postbox, network: self.network, peerId: self.peerId)
|> deliverOnMainQueue).start(next: { [weak self] stats in |> deliverOnMainQueue).start(next: { [weak self] stats in
if let strongSelf = self { if let strongSelf = self {
strongSelf._state = GroupStatsContextState(stats: stats) strongSelf._state = GroupStatsContextState(stats: stats)
@ -875,7 +855,7 @@ private final class GroupStatsContextImpl {
return return
} }
if case let .OnDemand(token) = stats.growthGraph { if case let .OnDemand(token) = stats.growthGraph {
self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token)
|> deliverOnMainQueue).start(next: { [weak self] graph in |> deliverOnMainQueue).start(next: { [weak self] graph in
if let strongSelf = self, let graph = graph { if let strongSelf = self, let graph = graph {
strongSelf._state = GroupStatsContextState(stats: strongSelf._state.stats?.withUpdatedGrowthGraph(graph)) strongSelf._state = GroupStatsContextState(stats: strongSelf._state.stats?.withUpdatedGrowthGraph(graph))
@ -890,7 +870,7 @@ private final class GroupStatsContextImpl {
return return
} }
if case let .OnDemand(token) = stats.membersGraph { if case let .OnDemand(token) = stats.membersGraph {
self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token)
|> deliverOnMainQueue).start(next: { [weak self] graph in |> deliverOnMainQueue).start(next: { [weak self] graph in
if let strongSelf = self, let graph = graph { if let strongSelf = self, let graph = graph {
strongSelf._state = GroupStatsContextState(stats: strongSelf._state.stats?.withUpdatedMembersGraph(graph)) strongSelf._state = GroupStatsContextState(stats: strongSelf._state.stats?.withUpdatedMembersGraph(graph))
@ -905,7 +885,7 @@ private final class GroupStatsContextImpl {
return return
} }
if case let .OnDemand(token) = stats.newMembersBySourceGraph { if case let .OnDemand(token) = stats.newMembersBySourceGraph {
self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token)
|> deliverOnMainQueue).start(next: { [weak self] graph in |> deliverOnMainQueue).start(next: { [weak self] graph in
if let strongSelf = self, let graph = graph { if let strongSelf = self, let graph = graph {
strongSelf._state = GroupStatsContextState(stats: strongSelf._state.stats?.withUpdatedNewMembersBySourceGraph(graph)) strongSelf._state = GroupStatsContextState(stats: strongSelf._state.stats?.withUpdatedNewMembersBySourceGraph(graph))
@ -920,7 +900,7 @@ private final class GroupStatsContextImpl {
return return
} }
if case let .OnDemand(token) = stats.languagesGraph { if case let .OnDemand(token) = stats.languagesGraph {
self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token)
|> deliverOnMainQueue).start(next: { [weak self] graph in |> deliverOnMainQueue).start(next: { [weak self] graph in
if let strongSelf = self, let graph = graph { if let strongSelf = self, let graph = graph {
strongSelf._state = GroupStatsContextState(stats: strongSelf._state.stats?.withUpdatedLanguagesGraph(graph)) strongSelf._state = GroupStatsContextState(stats: strongSelf._state.stats?.withUpdatedLanguagesGraph(graph))
@ -935,7 +915,7 @@ private final class GroupStatsContextImpl {
return return
} }
if case let .OnDemand(token) = stats.messagesGraph { if case let .OnDemand(token) = stats.messagesGraph {
self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token)
|> deliverOnMainQueue).start(next: { [weak self] graph in |> deliverOnMainQueue).start(next: { [weak self] graph in
if let strongSelf = self, let graph = graph { if let strongSelf = self, let graph = graph {
strongSelf._state = GroupStatsContextState(stats: strongSelf._state.stats?.withUpdatedMessagesGraph(graph)) strongSelf._state = GroupStatsContextState(stats: strongSelf._state.stats?.withUpdatedMessagesGraph(graph))
@ -950,7 +930,7 @@ private final class GroupStatsContextImpl {
return return
} }
if case let .OnDemand(token) = stats.actionsGraph { if case let .OnDemand(token) = stats.actionsGraph {
self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token)
|> deliverOnMainQueue).start(next: { [weak self] graph in |> deliverOnMainQueue).start(next: { [weak self] graph in
if let strongSelf = self, let graph = graph { if let strongSelf = self, let graph = graph {
strongSelf._state = GroupStatsContextState(stats: strongSelf._state.stats?.withUpdatedActionsGraph(graph)) strongSelf._state = GroupStatsContextState(stats: strongSelf._state.stats?.withUpdatedActionsGraph(graph))
@ -965,7 +945,7 @@ private final class GroupStatsContextImpl {
return return
} }
if case let .OnDemand(token) = stats.topHoursGraph { if case let .OnDemand(token) = stats.topHoursGraph {
self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token)
|> deliverOnMainQueue).start(next: { [weak self] graph in |> deliverOnMainQueue).start(next: { [weak self] graph in
if let strongSelf = self, let graph = graph { if let strongSelf = self, let graph = graph {
strongSelf._state = GroupStatsContextState(stats: strongSelf._state.stats?.withUpdatedTopHoursGraph(graph)) strongSelf._state = GroupStatsContextState(stats: strongSelf._state.stats?.withUpdatedTopHoursGraph(graph))
@ -980,7 +960,7 @@ private final class GroupStatsContextImpl {
return return
} }
if case let .OnDemand(token) = stats.topWeekdaysGraph { if case let .OnDemand(token) = stats.topWeekdaysGraph {
self.disposables.set((requestGraph(network: self.network, datacenterId: self.datacenterId, token: token) self.disposables.set((requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token)
|> deliverOnMainQueue).start(next: { [weak self] graph in |> deliverOnMainQueue).start(next: { [weak self] graph in
if let strongSelf = self, let graph = graph { if let strongSelf = self, let graph = graph {
strongSelf._state = GroupStatsContextState(stats: strongSelf._state.stats?.withUpdatedTopWeekdaysGraph(graph)) strongSelf._state = GroupStatsContextState(stats: strongSelf._state.stats?.withUpdatedTopWeekdaysGraph(graph))
@ -992,7 +972,7 @@ private final class GroupStatsContextImpl {
func loadDetailedGraph(_ graph: StatsGraph, x: Int64) -> Signal<StatsGraph?, NoError> { func loadDetailedGraph(_ graph: StatsGraph, x: Int64) -> Signal<StatsGraph?, NoError> {
if let token = graph.token { if let token = graph.token {
return requestGraph(network: self.network, datacenterId: self.datacenterId, token: token, x: x) return requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token, x: x)
} else { } else {
return .single(nil) return .single(nil)
} }
@ -1014,9 +994,9 @@ public final class GroupStatsContext {
} }
} }
public init(postbox: Postbox, network: Network, accountPeerId: PeerId, datacenterId: Int32, peerId: PeerId) { public init(postbox: Postbox, network: Network, accountPeerId: PeerId, peerId: PeerId) {
self.impl = QueueLocalObject(queue: Queue.mainQueue(), generate: { self.impl = QueueLocalObject(queue: Queue.mainQueue(), generate: {
return GroupStatsContextImpl(postbox: postbox, network: network, accountPeerId: accountPeerId, datacenterId: datacenterId, peerId: peerId) return GroupStatsContextImpl(postbox: postbox, network: network, accountPeerId: accountPeerId, peerId: peerId)
}) })
} }
@ -1147,41 +1127,47 @@ extension StatsPercentValue {
} }
} }
extension ChannelStatsMessageInteractions { extension ChannelStatsPostInteractions {
init(apiMessageInteractionCounters: Api.MessageInteractionCounters, peerId: PeerId) { init(apiPostInteractionCounters: Api.PostInteractionCounters, peerId: PeerId) {
switch apiMessageInteractionCounters { switch apiPostInteractionCounters {
case let .messageInteractionCounters(msgId, views, forwards): case let .postInteractionCountersMessage(msgId, views, forwards, reactions):
self = ChannelStatsMessageInteractions(messageId: MessageId(peerId: peerId, namespace: Namespaces.Message.Cloud, id: msgId), views: views, forwards: forwards, reactions: 0) self = ChannelStatsPostInteractions(postId: .message(id: EngineMessage.Id(peerId: peerId, namespace: Namespaces.Message.Cloud, id: msgId)), views: views, forwards: forwards, reactions: reactions)
case let .postInteractionCountersStory(storyId, views, forwards, reactions):
self = ChannelStatsPostInteractions(postId: .story(peerId: peerId, id: storyId), views: views, forwards: forwards, reactions: reactions)
} }
} }
} }
extension ChannelStats { extension ChannelStats {
convenience init(apiBroadcastStats: Api.stats.BroadcastStats, peerId: PeerId) { init(apiBroadcastStats: Api.stats.BroadcastStats, peerId: PeerId) {
switch apiBroadcastStats { switch apiBroadcastStats {
case let .broadcastStats(period, followers, viewsPerPost, sharesPerPost, enabledNotifications, apiGrowthGraph, apiFollowersGraph, apiMuteGraph, apiTopHoursGraph, apiInteractionsGraph, apiInstantViewInteractionsGraph, apiViewsBySourceGraph, apiNewFollowersBySourceGraph, apiLanguagesGraph, apiReactionsByEmotionGraph, apiStoryInteractionsGraph, apiStoryReactionsByEmotionGraph, recentMessageInteractions): case let .broadcastStats(period, followers, viewsPerPost, sharesPerPost, reactionsPerPost, viewsPerStory, sharesPerStory, reactionsPerStory, enabledNotifications, apiGrowthGraph, apiFollowersGraph, apiMuteGraph, apiTopHoursGraph, apiInteractionsGraph, apiInstantViewInteractionsGraph, apiViewsBySourceGraph, apiNewFollowersBySourceGraph, apiLanguagesGraph, apiReactionsByEmotionGraph, apiStoryInteractionsGraph, apiStoryReactionsByEmotionGraph, recentPostInteractions):
let growthGraph = StatsGraph(apiStatsGraph: apiGrowthGraph) let growthGraph = StatsGraph(apiStatsGraph: apiGrowthGraph)
let isEmpty = growthGraph.isEmpty let isEmpty = growthGraph.isEmpty
self.init( self.init(
period: StatsDateRange(apiStatsDateRangeDays: period), period: StatsDateRange(apiStatsDateRangeDays: period),
followers: StatsValue(apiStatsAbsValueAndPrev: followers), followers: StatsValue(apiStatsAbsValueAndPrev: followers),
viewsPerPost: StatsValue(apiStatsAbsValueAndPrev: viewsPerPost), viewsPerPost: StatsValue(apiStatsAbsValueAndPrev: viewsPerPost),
sharesPerPost: StatsValue(apiStatsAbsValueAndPrev: sharesPerPost), sharesPerPost: StatsValue(apiStatsAbsValueAndPrev: sharesPerPost),
enabledNotifications: StatsPercentValue(apiPercentValue: enabledNotifications), reactionsPerPost: StatsValue(apiStatsAbsValueAndPrev: reactionsPerPost),
growthGraph: growthGraph, viewsPerStory: StatsValue(apiStatsAbsValueAndPrev: viewsPerStory),
followersGraph: StatsGraph(apiStatsGraph: apiFollowersGraph), sharesPerStory: StatsValue(apiStatsAbsValueAndPrev: sharesPerStory),
muteGraph: StatsGraph(apiStatsGraph: apiMuteGraph), reactionsPerStory: StatsValue(apiStatsAbsValueAndPrev: reactionsPerStory),
topHoursGraph: StatsGraph(apiStatsGraph: apiTopHoursGraph), enabledNotifications: StatsPercentValue(apiPercentValue: enabledNotifications),
interactionsGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiInteractionsGraph), growthGraph: growthGraph,
instantPageInteractionsGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiInstantViewInteractionsGraph), followersGraph: StatsGraph(apiStatsGraph: apiFollowersGraph),
viewsBySourceGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiViewsBySourceGraph), muteGraph: StatsGraph(apiStatsGraph: apiMuteGraph),
newFollowersBySourceGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiNewFollowersBySourceGraph), topHoursGraph: StatsGraph(apiStatsGraph: apiTopHoursGraph),
languagesGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiLanguagesGraph), interactionsGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiInteractionsGraph),
reactionsByEmotionGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiReactionsByEmotionGraph), instantPageInteractionsGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiInstantViewInteractionsGraph),
storyInteractionsGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiStoryInteractionsGraph), viewsBySourceGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiViewsBySourceGraph),
storyReactionsByEmotionGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiStoryReactionsByEmotionGraph), newFollowersBySourceGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiNewFollowersBySourceGraph),
messageInteractions: recentMessageInteractions.map { ChannelStatsMessageInteractions(apiMessageInteractionCounters: $0, peerId: peerId) }) languagesGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiLanguagesGraph),
reactionsByEmotionGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiReactionsByEmotionGraph),
storyInteractionsGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiStoryInteractionsGraph),
storyReactionsByEmotionGraph: isEmpty ? .Empty : StatsGraph(apiStatsGraph: apiStoryReactionsByEmotionGraph),
postInteractions: recentPostInteractions.map { ChannelStatsPostInteractions(apiPostInteractionCounters: $0, peerId: peerId) })
} }
} }
} }
@ -1214,7 +1200,7 @@ extension GroupStatsTopInviter {
} }
extension GroupStats { extension GroupStats {
convenience init(apiMegagroupStats: Api.stats.MegagroupStats) { init(apiMegagroupStats: Api.stats.MegagroupStats) {
switch apiMegagroupStats { switch apiMegagroupStats {
case let .megagroupStats(period, members, messages, viewers, posters, apiGrowthGraph, apiMembersGraph, apiNewMembersBySourceGraph, apiLanguagesGraph, apiMessagesGraph, apiActionsGraph, apiTopHoursGraph, apiTopWeekdaysGraph, topPosters, topAdmins, topInviters, _): case let .megagroupStats(period, members, messages, viewers, posters, apiGrowthGraph, apiMembersGraph, apiNewMembersBySourceGraph, apiLanguagesGraph, apiMessagesGraph, apiActionsGraph, apiTopHoursGraph, apiTopWeekdaysGraph, topPosters, topAdmins, topInviters, _):
let growthGraph = StatsGraph(apiStatsGraph: apiGrowthGraph) let growthGraph = StatsGraph(apiStatsGraph: apiGrowthGraph)

View File

@ -47,18 +47,18 @@ public struct StoryStatsContextState: Equatable {
public var stats: StoryStats? public var stats: StoryStats?
} }
private func requestStoryStats(postbox: Postbox, network: Network, datacenterId: Int32, peerId: EnginePeer.Id, storyId: Int32, dark: Bool = false) -> Signal<StoryStats?, NoError> { private func requestStoryStats(postbox: Postbox, network: Network, peerId: EnginePeer.Id, storyId: Int32, dark: Bool = false) -> Signal<StoryStats?, NoError> {
return postbox.transaction { transaction -> Peer? in return postbox.transaction { transaction -> (Int32, Peer, Stories.Item)? in
if let peer = transaction.getPeer(peerId) { if let peer = transaction.getPeer(peerId), let storedItem = transaction.getStory(id: StoryId(peerId: peerId, id: storyId))?.get(Stories.StoredItem.self), case let .item(story) = storedItem, let cachedData = transaction.getPeerCachedData(peerId: peerId) as? CachedChannelData {
return peer return (cachedData.statsDatacenterId, peer, story)
} else { } else {
return nil return nil
} }
} |> mapToSignal { peer -> Signal<StoryStats?, NoError> in } |> mapToSignal { data -> Signal<StoryStats?, NoError> in
guard let peer = peer, let inputPeer = apiInputPeer(peer) else { guard let (statsDatacenterId, peer, story) = data, let inputPeer = apiInputPeer(peer) else {
return .never() return .never()
} }
var flags: Int32 = 0 var flags: Int32 = 0
if dark { if dark {
flags |= (1 << 1) flags |= (1 << 1)
@ -66,8 +66,8 @@ private func requestStoryStats(postbox: Postbox, network: Network, datacenterId:
let request = Api.functions.stats.getStoryStats(flags: flags, peer: inputPeer, id: storyId) let request = Api.functions.stats.getStoryStats(flags: flags, peer: inputPeer, id: storyId)
let signal: Signal<Api.stats.StoryStats, MTRpcError> let signal: Signal<Api.stats.StoryStats, MTRpcError>
if network.datacenterId != datacenterId { if network.datacenterId != statsDatacenterId {
signal = network.download(datacenterId: Int(datacenterId), isMedia: false, tag: nil) signal = network.download(datacenterId: Int(statsDatacenterId), isMedia: false, tag: nil)
|> castError(MTRpcError.self) |> castError(MTRpcError.self)
|> mapToSignal { worker in |> mapToSignal { worker in
return worker.request(request) return worker.request(request)
@ -76,15 +76,12 @@ private func requestStoryStats(postbox: Postbox, network: Network, datacenterId:
signal = network.request(request) signal = network.request(request)
} }
let views: Int = 0 var views: Int = 0
let forwards: Int = 0 var forwards: Int = 0
// for attribute in story.attributes { if let storyViews = story.views {
// if let viewsAttribute = attribute as? ViewCountStoryAttribute { views = storyViews.seenCount
// views = viewsAttribute.count forwards = storyViews.forwardCount
// } else if let forwardsAttribute = attribute as? ForwardCountStoryAttribute { }
// forwards = forwardsAttribute.count
// }
// }
return signal return signal
|> mapToSignal { result -> Signal<StoryStats?, MTRpcError> in |> mapToSignal { result -> Signal<StoryStats?, MTRpcError> in
@ -125,7 +122,6 @@ private func requestStoryStats(postbox: Postbox, network: Network, datacenterId:
private final class StoryStatsContextImpl { private final class StoryStatsContextImpl {
private let postbox: Postbox private let postbox: Postbox
private let network: Network private let network: Network
private let datacenterId: Int32
private let peerId: EnginePeer.Id private let peerId: EnginePeer.Id
private let storyId: Int32 private let storyId: Int32
@ -144,12 +140,11 @@ private final class StoryStatsContextImpl {
private let disposable = MetaDisposable() private let disposable = MetaDisposable()
private let disposables = DisposableDict<String>() private let disposables = DisposableDict<String>()
init(postbox: Postbox, network: Network, datacenterId: Int32, peerId: EnginePeer.Id, storyId: Int32) { init(postbox: Postbox, network: Network, peerId: EnginePeer.Id, storyId: Int32) {
assert(Queue.mainQueue().isCurrent()) assert(Queue.mainQueue().isCurrent())
self.postbox = postbox self.postbox = postbox
self.network = network self.network = network
self.datacenterId = datacenterId
self.peerId = peerId self.peerId = peerId
self.storyId = storyId self.storyId = storyId
self._state = StoryStatsContextState(stats: nil) self._state = StoryStatsContextState(stats: nil)
@ -167,7 +162,7 @@ private final class StoryStatsContextImpl {
private func load() { private func load() {
assert(Queue.mainQueue().isCurrent()) assert(Queue.mainQueue().isCurrent())
self.disposable.set((requestStoryStats(postbox: self.postbox, network: self.network, datacenterId: self.datacenterId, peerId: self.peerId, storyId: self.storyId) self.disposable.set((requestStoryStats(postbox: self.postbox, network: self.network, peerId: self.peerId, storyId: self.storyId)
|> deliverOnMainQueue).start(next: { [weak self] stats in |> deliverOnMainQueue).start(next: { [weak self] stats in
if let strongSelf = self { if let strongSelf = self {
strongSelf._state = StoryStatsContextState(stats: stats) strongSelf._state = StoryStatsContextState(stats: stats)
@ -178,7 +173,7 @@ private final class StoryStatsContextImpl {
func loadDetailedGraph(_ graph: StatsGraph, x: Int64) -> Signal<StatsGraph?, NoError> { func loadDetailedGraph(_ graph: StatsGraph, x: Int64) -> Signal<StatsGraph?, NoError> {
if let token = graph.token { if let token = graph.token {
return requestGraph(network: self.network, datacenterId: self.datacenterId, token: token, x: x) return requestGraph(postbox: self.postbox, network: self.network, peerId: self.peerId, token: token, x: x)
} else { } else {
return .single(nil) return .single(nil)
} }
@ -200,9 +195,9 @@ public final class StoryStatsContext {
} }
} }
public init(postbox: Postbox, network: Network, datacenterId: Int32, peerId: EnginePeer.Id, storyId: Int32) { public init(postbox: Postbox, network: Network, peerId: EnginePeer.Id, storyId: Int32) {
self.impl = QueueLocalObject(queue: Queue.mainQueue(), generate: { self.impl = QueueLocalObject(queue: Queue.mainQueue(), generate: {
return StoryStatsContextImpl(postbox: postbox, network: network, datacenterId: datacenterId, peerId: peerId, storyId: storyId) return StoryStatsContextImpl(postbox: postbox, network: network, peerId: peerId, storyId: storyId)
}) })
} }

View File

@ -9,7 +9,7 @@ public enum SearchMessagesLocation: Equatable {
case general(tags: MessageTags?, minDate: Int32?, maxDate: Int32?) case general(tags: MessageTags?, minDate: Int32?, maxDate: Int32?)
case group(groupId: PeerGroupId, tags: MessageTags?, minDate: Int32?, maxDate: Int32?) case group(groupId: PeerGroupId, tags: MessageTags?, minDate: Int32?, maxDate: Int32?)
case peer(peerId: PeerId, fromId: PeerId?, tags: MessageTags?, topMsgId: MessageId?, minDate: Int32?, maxDate: Int32?) case peer(peerId: PeerId, fromId: PeerId?, tags: MessageTags?, topMsgId: MessageId?, minDate: Int32?, maxDate: Int32?)
case publicForwards(messageId: MessageId, datacenterId: Int?) case publicForwards(messageId: MessageId)
case sentMedia(tags: MessageTags?) case sentMedia(tags: MessageTags?)
} }
@ -354,30 +354,31 @@ func _internal_searchMessages(account: Account, location: SearchMessagesLocation
return .single((nil, nil)) return .single((nil, nil))
} }
} }
case let .publicForwards(messageId, datacenterId): case let .publicForwards(messageId):
remoteSearchResult = account.postbox.transaction { transaction -> (Api.InputChannel?, Int32, MessageIndex?, Api.InputPeer) in remoteSearchResult = account.postbox.transaction { transaction -> (Api.InputChannel?, Int32, MessageIndex?, Api.InputPeer, Int32?) in
let sourcePeer = transaction.getPeer(messageId.peerId) let sourcePeer = transaction.getPeer(messageId.peerId)
let inputChannel = sourcePeer.flatMap { apiInputChannel($0) } let inputChannel = sourcePeer.flatMap { apiInputChannel($0) }
let statsDatacenterId = (transaction.getPeerCachedData(peerId: messageId.peerId) as? CachedChannelData)?.statsDatacenterId
var lowerBound: MessageIndex? var lowerBound: MessageIndex?
if let state = state, let message = state.main.messages.last { if let state = state, let message = state.main.messages.last {
lowerBound = message.index lowerBound = message.index
} }
if let lowerBound = lowerBound, let peer = transaction.getPeer(lowerBound.id.peerId), let inputPeer = apiInputPeer(peer) { if let lowerBound = lowerBound, let peer = transaction.getPeer(lowerBound.id.peerId), let inputPeer = apiInputPeer(peer) {
return (inputChannel, state?.main.nextRate ?? 0, lowerBound, inputPeer) return (inputChannel, state?.main.nextRate ?? 0, lowerBound, inputPeer, statsDatacenterId)
} else { } else {
return (inputChannel, 0, lowerBound, .inputPeerEmpty) return (inputChannel, 0, lowerBound, .inputPeerEmpty, statsDatacenterId)
} }
} }
|> mapToSignal { (inputChannel, nextRate, lowerBound, inputPeer) in |> mapToSignal { (inputChannel, nextRate, lowerBound, inputPeer, statsDatacenterId) in
guard let inputChannel = inputChannel else { guard let inputChannel = inputChannel else {
return .complete() return .complete()
} }
let request = Api.functions.stats.getMessagePublicForwards(channel: inputChannel, msgId: messageId.id, offsetRate: nextRate, offsetPeer: inputPeer, offsetId: lowerBound?.id.id ?? 0, limit: limit) let request = Api.functions.stats.getMessagePublicForwards(channel: inputChannel, msgId: messageId.id, offsetRate: nextRate, offsetPeer: inputPeer, offsetId: lowerBound?.id.id ?? 0, limit: limit)
let signal: Signal<Api.messages.Messages, MTRpcError> let signal: Signal<Api.messages.Messages, MTRpcError>
if let datacenterId = datacenterId, account.network.datacenterId != datacenterId { if let statsDatacenterId = statsDatacenterId, account.network.datacenterId != statsDatacenterId {
signal = account.network.download(datacenterId: datacenterId, isMedia: false, tag: nil) signal = account.network.download(datacenterId: Int(statsDatacenterId), isMedia: false, tag: nil)
|> castError(MTRpcError.self) |> castError(MTRpcError.self)
|> mapToSignal { worker in |> mapToSignal { worker in
return worker.request(request) return worker.request(request)

View File

@ -34,12 +34,12 @@ private func entryId(peerId: EnginePeer.Id) -> ItemCacheEntryId {
return ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.recommendedChannels, key: cacheKey) return ItemCacheEntryId(collectionId: Namespaces.CachedItemCollection.recommendedChannels, key: cacheKey)
} }
func _internal_requestRecommendedChannels(account: Account, peerId: EnginePeer.Id) -> Signal<Never, NoError> { func _internal_requestRecommendedChannels(account: Account, peerId: EnginePeer.Id, forceUpdate: Bool) -> Signal<Never, NoError> {
return account.postbox.transaction { transaction -> Peer? in return account.postbox.transaction { transaction -> Peer? in
guard let channel = transaction.getPeer(peerId) else { guard let channel = transaction.getPeer(peerId) as? TelegramChannel, case .broadcast = channel.info else {
return nil return nil
} }
if let entry = transaction.retrieveItemCacheEntry(id: entryId(peerId: peerId))?.get(CachedRecommendedChannels.self), !entry.peerIds.isEmpty { if let entry = transaction.retrieveItemCacheEntry(id: entryId(peerId: peerId))?.get(CachedRecommendedChannels.self), !entry.peerIds.isEmpty && !forceUpdate {
return nil return nil
} else { } else {
return channel return channel

View File

@ -79,7 +79,7 @@ func _internal_joinChannel(account: Account, peerId: PeerId, hash: String?) -> S
} }
|> afterCompleted { |> afterCompleted {
if hash == nil { if hash == nil {
let _ = _internal_requestRecommendedChannels(account: account, peerId: peerId).startStandalone() let _ = _internal_requestRecommendedChannels(account: account, peerId: peerId, forceUpdate: true).startStandalone()
} }
} }
} else { } else {

View File

@ -1249,8 +1249,8 @@ public extension TelegramEngine {
return _internal_toggleRecommendedChannelsHidden(account: self.account, peerId: peerId, hidden: hidden) return _internal_toggleRecommendedChannelsHidden(account: self.account, peerId: peerId, hidden: hidden)
} }
public func requestRecommendedChannels(peerId: EnginePeer.Id) -> Signal<Never, NoError> { public func requestRecommendedChannels(peerId: EnginePeer.Id, forceUpdate: Bool = false) -> Signal<Never, NoError> {
return _internal_requestRecommendedChannels(account: self.account, peerId: peerId) return _internal_requestRecommendedChannels(account: self.account, peerId: peerId, forceUpdate: forceUpdate)
} }
} }
} }

View File

@ -211,7 +211,7 @@ public final class BlockedPeersContext {
} }
} }
let inputPeers = peers.compactMap { apiInputPeer($0) } let inputPeers = peers.compactMap { apiInputPeer($0) }
return network.request(Api.functions.contacts.setBlocked(flags: flags, id: inputPeers, limit: Int32(peers.count))) return network.request(Api.functions.contacts.setBlocked(flags: flags, id: inputPeers, limit: Int32(max(currentPeers.count, peers.count))))
|> mapError { _ -> BlockedPeersContextAddError in |> mapError { _ -> BlockedPeersContextAddError in
return .generic return .generic
} }

View File

@ -370,24 +370,11 @@ final class PeerAllowedReactionsScreenComponent: Component {
} }
private func openBoostStats() { private func openBoostStats() {
guard let component = self.component else { guard let component = self.component, let boostStatus = self.boostStatus else {
return return
} }
let statsController = component.context.sharedContext.makeChannelStatsController(context: component.context, updatedPresentationData: nil, peerId: component.peerId, boosts: true, boostStatus: boostStatus)
let _ = (component.context.engine.data.get( self.environment?.controller()?.push(statsController)
TelegramEngine.EngineData.Item.Peer.StatsDatacenterId(id: component.peerId)
)
|> deliverOnMainQueue).start(next: { [weak self] statsDatacenterId in
guard let self, let component = self.component, let boostStatus = self.boostStatus else {
return
}
guard let statsDatacenterId else {
return
}
let statsController = component.context.sharedContext.makeChannelStatsController(context: component.context, updatedPresentationData: nil, peerId: component.peerId, boosts: true, boostStatus: boostStatus, statsDatacenterId: statsDatacenterId)
self.environment?.controller()?.push(statsController)
})
} }
func update(component: PeerAllowedReactionsScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize { func update(component: PeerAllowedReactionsScreenComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: Transition) -> CGSize {

View File

@ -890,14 +890,18 @@ func peerInfoScreenData(context: AccountContext, peerId: PeerId, strings: Presen
requestsContextPromise.get(), requestsContextPromise.get(),
requestsStatePromise.get(), requestsStatePromise.get(),
hasStories, hasStories,
accountIsPremium accountIsPremium,
context.engine.peers.recommendedChannels(peerId: peerId)
) )
|> map { peerView, availablePanes, globalNotificationSettings, status, currentInvitationsContext, invitations, currentRequestsContext, requests, hasStories, accountIsPremium -> PeerInfoScreenData in |> map { peerView, availablePanes, globalNotificationSettings, status, currentInvitationsContext, invitations, currentRequestsContext, requests, hasStories, accountIsPremium, recommendedChannels -> PeerInfoScreenData in
var availablePanes = availablePanes var availablePanes = availablePanes
if let hasStories { if let hasStories {
if hasStories { if hasStories {
availablePanes?.insert(.stories, at: 0) availablePanes?.insert(.stories, at: 0)
} }
if let recommendedChannels, !recommendedChannels.channels.isEmpty {
availablePanes?.append(.recommended)
}
} else { } else {
availablePanes = nil availablePanes = nil
} }

View File

@ -416,6 +416,8 @@ private final class PeerInfoPendingPane {
} else { } else {
preconditionFailure() preconditionFailure()
} }
case .recommended:
paneNode = PeerInfoRecommendedChannelsPaneNode(context: context, peerId: peerId, chatControllerInteraction: chatControllerInteraction)
} }
paneNode.parentController = parentController paneNode.parentController = parentController
self.pane = PeerInfoPaneWrapper(key: key, node: paneNode) self.pane = PeerInfoPaneWrapper(key: key, node: paneNode)
@ -989,6 +991,8 @@ final class PeerInfoPaneContainerNode: ASDisplayNode, UIGestureRecognizerDelegat
title = presentationData.strings.PeerInfo_PaneGroups title = presentationData.strings.PeerInfo_PaneGroups
case .members: case .members:
title = presentationData.strings.PeerInfo_PaneMembers title = presentationData.strings.PeerInfo_PaneMembers
case .recommended:
title = presentationData.strings.PeerInfo_PaneRecommended
} }
return PeerInfoPaneSpecifier(key: key, title: title) return PeerInfoPaneSpecifier(key: key, title: title)
}, selectedPane: self.currentPaneKey, transitionFraction: self.transitionFraction, transition: transition) }, selectedPane: self.currentPaneKey, transitionFraction: self.transitionFraction, transition: transition)

View File

@ -3990,6 +3990,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|> deliverOnMainQueue).startStrict(next: { [weak self] translationState in |> deliverOnMainQueue).startStrict(next: { [weak self] translationState in
self?.translationState = translationState self?.translationState = translationState
}) })
let _ = context.engine.peers.requestRecommendedChannels(peerId: peerId, forceUpdate: true).startStandalone()
} }
if peerId.namespace == Namespaces.Peer.CloudChannel || peerId.namespace == Namespaces.Peer.CloudUser { if peerId.namespace == Namespaces.Peer.CloudChannel || peerId.namespace == Namespaces.Peer.CloudUser {
@ -6735,21 +6737,16 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
} }
private func openStats(boosts: Bool = false, boostStatus: ChannelBoostStatus? = nil) { private func openStats(boosts: Bool = false, boostStatus: ChannelBoostStatus? = nil) {
guard let controller = self.controller, let data = self.data, let peer = data.peer, let cachedData = data.cachedData else { guard let controller = self.controller, let data = self.data, let peer = data.peer else {
return return
} }
self.view.endEditing(true) self.view.endEditing(true)
var statsDatacenterId: Int32?
if let cachedData = cachedData as? CachedChannelData {
statsDatacenterId = cachedData.statsDatacenterId
}
let statsController: ViewController let statsController: ViewController
if let channel = peer as? TelegramChannel, case .group = channel.info { if let channel = peer as? TelegramChannel, case .group = channel.info {
statsController = groupStatsController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, peerId: peer.id, statsDatacenterId: statsDatacenterId) statsController = groupStatsController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, peerId: peer.id)
} else { } else {
statsController = channelStatsController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, peerId: peer.id, section: boosts ? .boosts : .stats, boostStatus: boostStatus, statsDatacenterId: statsDatacenterId) statsController = channelStatsController(context: self.context, updatedPresentationData: self.controller?.updatedPresentationData, peerId: peer.id, section: boosts ? .boosts : .stats, boostStatus: boostStatus)
} }
controller.push(statsController) controller.push(statsController)
} }

View File

@ -129,23 +129,42 @@ public final class StoryContentContextImpl: StoryContentContext {
peerPresence = presencesView.presences[peerId] peerPresence = presencesView.presences[peerId]
} }
if let cachedPeerDataView = views.views[PostboxViewKey.cachedPeerData(peerId: peerId)] as? CachedPeerDataView, let cachedUserData = cachedPeerDataView.cachedPeerData as? CachedUserData { if let cachedPeerDataView = views.views[PostboxViewKey.cachedPeerData(peerId: peerId)] as? CachedPeerDataView {
var isMuted = false if let cachedUserData = cachedPeerDataView.cachedPeerData as? CachedUserData {
if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings { var isMuted = false
isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer._asPeer(), peerSettings: notificationSettings, topSearchPeers: []) if let notificationSettings = peerView.notificationSettings as? TelegramPeerNotificationSettings {
isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer._asPeer(), peerSettings: notificationSettings, topSearchPeers: [])
} else {
isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer._asPeer(), peerSettings: nil, topSearchPeers: [])
}
additionalPeerData = StoryContentContextState.AdditionalPeerData(
isMuted: isMuted,
areVoiceMessagesAvailable: cachedUserData.voiceMessagesAvailable,
presence: peerPresence.flatMap { EnginePeer.Presence($0) },
canViewStats: false
)
} else if let cachedChannelData = cachedPeerDataView.cachedPeerData as? CachedChannelData {
additionalPeerData = StoryContentContextState.AdditionalPeerData(
isMuted: true,
areVoiceMessagesAvailable: true,
presence: peerPresence.flatMap { EnginePeer.Presence($0) },
canViewStats: cachedChannelData.flags.contains(.canViewStats)
)
} else { } else {
isMuted = resolvedAreStoriesMuted(globalSettings: globalNotificationSettings._asGlobalNotificationSettings(), peer: peer._asPeer(), peerSettings: nil, topSearchPeers: []) additionalPeerData = StoryContentContextState.AdditionalPeerData(
isMuted: true,
areVoiceMessagesAvailable: true,
presence: peerPresence.flatMap { EnginePeer.Presence($0) },
canViewStats: false
)
} }
additionalPeerData = StoryContentContextState.AdditionalPeerData( }
isMuted: isMuted, else {
areVoiceMessagesAvailable: cachedUserData.voiceMessagesAvailable,
presence: peerPresence.flatMap { EnginePeer.Presence($0) }
)
} else {
additionalPeerData = StoryContentContextState.AdditionalPeerData( additionalPeerData = StoryContentContextState.AdditionalPeerData(
isMuted: true, isMuted: true,
areVoiceMessagesAvailable: true, areVoiceMessagesAvailable: true,
presence: peerPresence.flatMap { EnginePeer.Presence($0) } presence: peerPresence.flatMap { EnginePeer.Presence($0) },
canViewStats: false
) )
} }
let state = stateView.value?.get(Stories.PeerState.self) let state = stateView.value?.get(Stories.PeerState.self)
@ -993,6 +1012,7 @@ public final class SingleStoryContentContextImpl: StoryContentContext {
TelegramEngine.EngineData.Item.Peer.Peer(id: storyId.peerId), TelegramEngine.EngineData.Item.Peer.Peer(id: storyId.peerId),
TelegramEngine.EngineData.Item.Peer.Presence(id: storyId.peerId), TelegramEngine.EngineData.Item.Peer.Presence(id: storyId.peerId),
TelegramEngine.EngineData.Item.Peer.AreVoiceMessagesAvailable(id: storyId.peerId), TelegramEngine.EngineData.Item.Peer.AreVoiceMessagesAvailable(id: storyId.peerId),
TelegramEngine.EngineData.Item.Peer.CanViewStats(id: storyId.peerId),
TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: storyId.peerId), TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: storyId.peerId),
TelegramEngine.EngineData.Item.NotificationSettings.Global() TelegramEngine.EngineData.Item.NotificationSettings.Global()
), ),
@ -1045,7 +1065,7 @@ public final class SingleStoryContentContextImpl: StoryContentContext {
return return
} }
let (peer, presence, areVoiceMessagesAvailable, notificationSettings, globalNotificationSettings) = data let (peer, presence, areVoiceMessagesAvailable, canViewStats, notificationSettings, globalNotificationSettings) = data
let (item, peers, allEntityFiles) = itemAndPeers let (item, peers, allEntityFiles) = itemAndPeers
guard let peer else { guard let peer else {
@ -1057,7 +1077,8 @@ public final class SingleStoryContentContextImpl: StoryContentContext {
let additionalPeerData = StoryContentContextState.AdditionalPeerData( let additionalPeerData = StoryContentContextState.AdditionalPeerData(
isMuted: isMuted, isMuted: isMuted,
areVoiceMessagesAvailable: areVoiceMessagesAvailable, areVoiceMessagesAvailable: areVoiceMessagesAvailable,
presence: presence presence: presence,
canViewStats: canViewStats
) )
if item == nil { if item == nil {
@ -1201,6 +1222,7 @@ public final class PeerStoryListContentContextImpl: StoryContentContext {
TelegramEngine.EngineData.Item.Peer.Peer(id: peerId), TelegramEngine.EngineData.Item.Peer.Peer(id: peerId),
TelegramEngine.EngineData.Item.Peer.Presence(id: peerId), TelegramEngine.EngineData.Item.Peer.Presence(id: peerId),
TelegramEngine.EngineData.Item.Peer.AreVoiceMessagesAvailable(id: peerId), TelegramEngine.EngineData.Item.Peer.AreVoiceMessagesAvailable(id: peerId),
TelegramEngine.EngineData.Item.Peer.CanViewStats(id: peerId),
TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: peerId), TelegramEngine.EngineData.Item.Peer.NotificationSettings(id: peerId),
TelegramEngine.EngineData.Item.NotificationSettings.Global() TelegramEngine.EngineData.Item.NotificationSettings.Global()
), ),
@ -1212,7 +1234,7 @@ public final class PeerStoryListContentContextImpl: StoryContentContext {
return return
} }
let (peer, presence, areVoiceMessagesAvailable, notificationSettings, globalNotificationSettings) = data let (peer, presence, areVoiceMessagesAvailable, canViewStats, notificationSettings, globalNotificationSettings) = data
guard let peer else { guard let peer else {
return return
@ -1223,7 +1245,8 @@ public final class PeerStoryListContentContextImpl: StoryContentContext {
let additionalPeerData = StoryContentContextState.AdditionalPeerData( let additionalPeerData = StoryContentContextState.AdditionalPeerData(
isMuted: isMuted, isMuted: isMuted,
areVoiceMessagesAvailable: areVoiceMessagesAvailable, areVoiceMessagesAvailable: areVoiceMessagesAvailable,
presence: presence presence: presence,
canViewStats: canViewStats
) )
self.listState = state self.listState = state

View File

@ -140,15 +140,18 @@ public final class StoryContentContextState {
public let isMuted: Bool public let isMuted: Bool
public let areVoiceMessagesAvailable: Bool public let areVoiceMessagesAvailable: Bool
public let presence: EnginePeer.Presence? public let presence: EnginePeer.Presence?
public let canViewStats: Bool
public init( public init(
isMuted: Bool, isMuted: Bool,
areVoiceMessagesAvailable: Bool, areVoiceMessagesAvailable: Bool,
presence: EnginePeer.Presence? presence: EnginePeer.Presence?,
canViewStats: Bool
) { ) {
self.isMuted = isMuted self.isMuted = isMuted
self.areVoiceMessagesAvailable = areVoiceMessagesAvailable self.areVoiceMessagesAvailable = areVoiceMessagesAvailable
self.presence = presence self.presence = presence
self.canViewStats = canViewStats
} }
public static func == (lhs: StoryContentContextState.AdditionalPeerData, rhs: StoryContentContextState.AdditionalPeerData) -> Bool { public static func == (lhs: StoryContentContextState.AdditionalPeerData, rhs: StoryContentContextState.AdditionalPeerData) -> Bool {
@ -161,6 +164,9 @@ public final class StoryContentContextState {
if lhs.presence != rhs.presence { if lhs.presence != rhs.presence {
return false return false
} }
if lhs.canViewStats != rhs.canViewStats {
return false
}
return true return true
} }
} }

View File

@ -1796,6 +1796,12 @@ public final class StoryItemSetContainerComponent: Component {
return return
} }
self.sendMessageContext.performShareAction(view: self) self.sendMessageContext.performShareAction(view: self)
},
repostAction: { [weak self] in
guard let self else {
return
}
self.openStoryEditing(repost: true)
} }
)), )),
environment: {}, environment: {},
@ -2980,6 +2986,7 @@ public final class StoryItemSetContainerComponent: Component {
audioRecorder: self.sendMessageContext.audioRecorderValue, audioRecorder: self.sendMessageContext.audioRecorderValue,
videoRecordingStatus: !self.sendMessageContext.hasRecordedVideoPreview ? self.sendMessageContext.videoRecorderValue?.audioStatus : nil, videoRecordingStatus: !self.sendMessageContext.hasRecordedVideoPreview ? self.sendMessageContext.videoRecorderValue?.audioStatus : nil,
isRecordingLocked: self.sendMessageContext.isMediaRecordingLocked, isRecordingLocked: self.sendMessageContext.isMediaRecordingLocked,
hasRecordedVideo: false,
recordedAudioPreview: self.sendMessageContext.recordedAudioPreview, recordedAudioPreview: self.sendMessageContext.recordedAudioPreview,
hasRecordedVideoPreview: self.sendMessageContext.hasRecordedVideoPreview, hasRecordedVideoPreview: self.sendMessageContext.hasRecordedVideoPreview,
wasRecordingDismissed: self.sendMessageContext.wasRecordingDismissed, wasRecordingDismissed: self.sendMessageContext.wasRecordingDismissed,
@ -5075,7 +5082,7 @@ public final class StoryItemSetContainerComponent: Component {
StoryContainerScreen.openPeerStories(context: component.context, peerId: peer.id, parentController: controller, avatarNode: avatarNode) StoryContainerScreen.openPeerStories(context: component.context, peerId: peer.id, parentController: controller, avatarNode: avatarNode)
} }
private func openStoryEditing() { func openStoryEditing(repost: Bool = false) {
guard let component = self.component, let peerReference = PeerReference(component.slice.peer._asPeer()) else { guard let component = self.component, let peerReference = PeerReference(component.slice.peer._asPeer()) else {
return return
} }
@ -5955,6 +5962,26 @@ public final class StoryItemSetContainerComponent: Component {
}))) })))
} }
if component.slice.additionalPeerData.canViewStats {
items.append(.action(ContextMenuActionItem(text: component.strings.Story_Context_ViewStats, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Statistics"), color: theme.contextMenu.primaryColor)
}, action: { [weak self] _, a in
a(.default)
guard let self, let component = self.component else {
return
}
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }.withUpdated(theme: defaultDarkColorPresentationTheme)
let statsController = component.context.sharedContext.makeStoryStatsController(
context: component.context,
updatedPresentationData: (presentationData, .single(presentationData)),
peerId: component.slice.peer.id,
storyId: component.slice.item.storyItem.id
)
component.controller()?.push(statsController)
})))
}
let saveText: String = component.strings.Story_Context_SaveToGallery let saveText: String = component.strings.Story_Context_SaveToGallery
items.append(.action(ContextMenuActionItem(text: saveText, icon: { theme in items.append(.action(ContextMenuActionItem(text: saveText, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Save"), color: theme.contextMenu.primaryColor) return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Save"), color: theme.contextMenu.primaryColor)

View File

@ -2047,7 +2047,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}) })
|> deliverOnMainQueue).startStrict(error: { error in |> deliverOnMainQueue).startStrict(error: { error in
let controller = ownershipTransferController(context: context, updatedPresentationData: strongSelf.updatedPresentationData, initialError: error, present: { c, a in let controller = ownershipTransferController(context: context, updatedPresentationData: strongSelf.updatedPresentationData, initialError: error, present: { c, a in
strongSelf.present(c, in: .window(.root), with: a) strongSelf.present(c, in: .window(.root), with: a)
}, commit: { password in }, commit: { password in
return context.engine.messages.requestMessageActionCallback(messageId: messageId, isGame: isGame, password: password, data: data) return context.engine.messages.requestMessageActionCallback(messageId: messageId, isGame: isGame, password: password, data: data)
|> afterDisposed { |> afterDisposed {
@ -3680,21 +3680,18 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let _ = strongSelf.presentVoiceMessageDiscardAlert(action: { let _ = strongSelf.presentVoiceMessageDiscardAlert(action: {
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: id)) let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Messages.Message(id: id))
|> mapToSignal { message -> Signal<(EngineMessage.Id, Int32?)?, NoError> in |> mapToSignal { message -> Signal<EngineMessage.Id?, NoError> in
if let message { if let message {
return context.engine.data.get(TelegramEngine.EngineData.Item.Peer.StatsDatacenterId(id: message.id.peerId)) return .single(message.id)
|> map { statsDatacenterId -> (EngineMessage.Id, Int32?)? in
return (message.id, statsDatacenterId)
}
} else { } else {
return .complete() return .complete()
} }
} }
|> deliverOnMainQueue).startStandalone(next: { [weak self] messageIdAndStatsDatacenterId in |> deliverOnMainQueue).startStandalone(next: { [weak self] messageId in
guard let strongSelf = self, let (id, statsDatacenterId) = messageIdAndStatsDatacenterId, let statsDatacenterId = statsDatacenterId else { guard let strongSelf = self, let messageId else {
return return
} }
strongSelf.push(messageStatsController(context: context, subject: .message(id: id), statsDatacenterId: statsDatacenterId)) strongSelf.push(messageStatsController(context: context, subject: .message(id: messageId)))
}) })
}, delay: true) }, delay: true)
}, editMessageMedia: { [weak self] messageId, draw in }, editMessageMedia: { [weak self] messageId, draw in
@ -4674,10 +4671,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
galleryController.setHintWillBePresentedInPreviewingContext(true) galleryController.setHintWillBePresentedInPreviewingContext(true)
let items: Signal<[ContextMenuItem], NoError> = context.engine.data.get( let items: Signal<[ContextMenuItem], NoError> = context.engine.data.get(
TelegramEngine.EngineData.Item.Peer.CanViewStats(id: peer.id), TelegramEngine.EngineData.Item.Peer.CanViewStats(id: peer.id)
TelegramEngine.EngineData.Item.Peer.StatsDatacenterId(id: peer.id)
) )
|> map { canViewStats, statsDatacenterId -> [ContextMenuItem] in |> map { canViewStats -> [ContextMenuItem] in
var items: [ContextMenuItem] = [ var items: [ContextMenuItem] = [
.action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_LinkDialogOpen, icon: { theme in .action(ContextMenuActionItem(text: strongSelf.presentationData.strings.Conversation_LinkDialogOpen, icon: { theme in
return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.actionSheet.primaryTextColor) return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Info"), color: theme.actionSheet.primaryTextColor)
@ -4698,9 +4694,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
let statsController: ViewController let statsController: ViewController
if let channel = peer as? TelegramChannel, case .group = channel.info { if let channel = peer as? TelegramChannel, case .group = channel.info {
statsController = groupStatsController(context: context, updatedPresentationData: strongSelf.updatedPresentationData, peerId: peer.id, statsDatacenterId: statsDatacenterId) statsController = groupStatsController(context: context, updatedPresentationData: strongSelf.updatedPresentationData, peerId: peer.id)
} else { } else {
statsController = channelStatsController(context: context, updatedPresentationData: strongSelf.updatedPresentationData, peerId: peer.id, statsDatacenterId: statsDatacenterId) statsController = channelStatsController(context: context, updatedPresentationData: strongSelf.updatedPresentationData, peerId: peer.id)
} }
strongSelf.push(statsController) strongSelf.push(statsController)
}))) })))

View File

@ -1870,8 +1870,16 @@ public final class SharedAccountContextImpl: SharedAccountContext {
return installedStickerPacksController(context: context, mode: mode, forceTheme: forceTheme) return installedStickerPacksController(context: context, mode: mode, forceTheme: forceTheme)
} }
public func makeChannelStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peerId: EnginePeer.Id, boosts: Bool, boostStatus: ChannelBoostStatus?, statsDatacenterId: Int32) -> ViewController { public func makeChannelStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peerId: EnginePeer.Id, boosts: Bool, boostStatus: ChannelBoostStatus?) -> ViewController {
return channelStatsController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, section: boosts ? .boosts : .stats, boostStatus: nil, statsDatacenterId: statsDatacenterId) return channelStatsController(context: context, updatedPresentationData: updatedPresentationData, peerId: peerId, section: boosts ? .boosts : .stats, boostStatus: boostStatus)
}
public func makeMessagesStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, messageId: EngineMessage.Id) -> ViewController {
return messageStatsController(context: context, updatedPresentationData: updatedPresentationData, subject: .message(id: messageId))
}
public func makeStoryStatsController(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, peerId: EnginePeer.Id, storyId: Int32) -> ViewController {
return messageStatsController(context: context, updatedPresentationData: updatedPresentationData, subject: .story(peerId: peerId, id: storyId))
} }
} }

View File

@ -747,8 +747,6 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
private let dimLayer: SimpleLayer private let dimLayer: SimpleLayer
private var isGeneratingPatternImage: Bool = false private var isGeneratingPatternImage: Bool = false
private let bakedBackgroundView: UIImageView
private var validLayout: (CGSize, WallpaperDisplayMode)? private var validLayout: (CGSize, WallpaperDisplayMode)?
private var wallpaper: TelegramWallpaper? private var wallpaper: TelegramWallpaper?
private var isSettingUpWallpaper: Bool = false private var isSettingUpWallpaper: Bool = false
@ -861,9 +859,6 @@ final class WallpaperBackgroundNodeImpl: ASDisplayNode, WallpaperBackgroundNode
self.patternImageLayer = EffectImageLayer() self.patternImageLayer = EffectImageLayer()
self.bakedBackgroundView = UIImageView()
self.bakedBackgroundView.isHidden = true
self.dimLayer = SimpleLayer() self.dimLayer = SimpleLayer()
self.dimLayer.opacity = 0.0 self.dimLayer.opacity = 0.0
self.dimLayer.backgroundColor = UIColor.black.cgColor self.dimLayer.backgroundColor = UIColor.black.cgColor